@circle-fin/bridge-kit 1.0.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/index.mjs ADDED
@@ -0,0 +1,4858 @@
1
+ /**
2
+ * Copyright (c) 2025, Circle Internet Group, Inc. All rights reserved.
3
+ *
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ *
6
+ * Licensed under the Apache License, Version 2.0 (the "License");
7
+ * you may not use this file except in compliance with the License.
8
+ * You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing, software
13
+ * distributed under the License is distributed on an "AS IS" BASIS,
14
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ * See the License for the specific language governing permissions and
16
+ * limitations under the License.
17
+ */
18
+
19
+ import { z } from 'zod';
20
+ import '@ethersproject/bytes';
21
+ import '@ethersproject/address';
22
+ import 'bs58';
23
+ import { parseUnits as parseUnits$1 } from '@ethersproject/units';
24
+ import { CCTPV2BridgingProvider } from '@circle-fin/provider-cctp-v2';
25
+
26
+ /**
27
+ * Custom error class for validation errors.
28
+ * Provides structured error information while hiding implementation details.
29
+ */
30
+ class ValidationError extends Error {
31
+ errors;
32
+ constructor(message, errors) {
33
+ super(message);
34
+ this.errors = errors;
35
+ this.name = 'ValidationError';
36
+ }
37
+ }
38
+
39
+ /**
40
+ * Formats a Zod error for display.
41
+ * @internal
42
+ */
43
+ const formatZodError = (error) => {
44
+ const path = error.path.length > 0 ? `${error.path.join('.')}: ` : '';
45
+ return `${path}${error.message}`;
46
+ };
47
+ /**
48
+ * Validates data against a Zod schema with enhanced error reporting.
49
+ *
50
+ * This function performs validation using Zod schemas and provides detailed error
51
+ * messages that include the validation context. It's designed to give developers
52
+ * clear feedback about what went wrong during validation.
53
+ *
54
+ * @param schema - The Zod schema to validate against
55
+ * @param data - The data to validate
56
+ * @param context - Context string to include in error messages (e.g., 'bridge parameters')
57
+ * @returns The validated and parsed data
58
+ * @throws {ValidationError} If validation fails
59
+ *
60
+ * @example
61
+ * ```typescript
62
+ * const result = validate(BridgeParamsSchema, params, 'bridge parameters')
63
+ * ```
64
+ */
65
+ function validate(value, schema, context) {
66
+ const result = schema.safeParse(value);
67
+ if (!result.success) {
68
+ const errors = result.error.errors.map(formatZodError);
69
+ const firstError = errors[0] ?? 'Invalid value';
70
+ throw new ValidationError(`Invalid ${context}: ${firstError}`, errors);
71
+ }
72
+ }
73
+
74
+ /**
75
+ * Symbol used to track validation state on objects.
76
+ * This allows us to attach metadata to objects without interfering with their structure,
77
+ * enabling optimized validation by skipping already validated objects.
78
+ * @internal
79
+ */
80
+ const VALIDATION_STATE = Symbol('validationState');
81
+ /**
82
+ * Validates data against a Zod schema with state tracking and enhanced error reporting.
83
+ *
84
+ * This function performs validation using Zod schemas while tracking validation state
85
+ * and providing detailed error messages. It's designed for use in scenarios where
86
+ * validation state needs to be monitored and reported.
87
+ *
88
+ * @param schema - The Zod schema to validate against
89
+ * @param data - The data to validate
90
+ * @param context - Context string to include in error messages (e.g., 'bridge parameters')
91
+ * @returns Object containing validation result and state information
92
+ * @throws {ValidationError} If validation fails
93
+ *
94
+ * @example
95
+ * ```typescript
96
+ * const result = validateWithStateTracking(BridgeParamsSchema, params, 'bridge parameters')
97
+ * ```
98
+ */
99
+ function validateWithStateTracking(value, schema, context, validatorName) {
100
+ // Skip validation for null or undefined values
101
+ if (value === null) {
102
+ throw new ValidationError(`Invalid ${context}: Value is null`, [
103
+ `Value is null`,
104
+ ]);
105
+ }
106
+ if (value === undefined) {
107
+ throw new ValidationError(`Invalid ${context}: Value is undefined`, [
108
+ `Value is undefined`,
109
+ ]);
110
+ }
111
+ // Ensure value is an object that can hold validation state
112
+ if (typeof value !== 'object') {
113
+ throw new ValidationError(`Invalid ${context}: Value must be an object`, [
114
+ `Value must be an object, got ${typeof value}`,
115
+ ]);
116
+ }
117
+ // Get or initialize validation state
118
+ const valueWithState = value;
119
+ const state = valueWithState[VALIDATION_STATE] ?? { validatedBy: [] };
120
+ // Skip validation if already validated by this validator
121
+ if (state.validatedBy.includes(validatorName)) {
122
+ return;
123
+ }
124
+ // Delegate to the validate function for actual validation
125
+ validate(value, schema, context);
126
+ // Update validation state
127
+ state.validatedBy.push(validatorName);
128
+ valueWithState[VALIDATION_STATE] = state;
129
+ }
130
+
131
+ /**
132
+ * Zod schema for validating chain definition objects used in buildExplorerUrl.
133
+ * This schema ensures the chain definition has the required properties for URL generation.
134
+ */
135
+ const chainDefinitionSchema$1 = z.object({
136
+ name: z
137
+ .string({
138
+ required_error: 'Chain name is required',
139
+ invalid_type_error: 'Chain name must be a string',
140
+ })
141
+ .min(1, 'Chain name cannot be empty'),
142
+ explorerUrl: z
143
+ .string({
144
+ required_error: 'Explorer URL template is required',
145
+ invalid_type_error: 'Explorer URL template must be a string',
146
+ })
147
+ .min(1, 'Explorer URL template cannot be empty')
148
+ .refine((url) => url.includes('{hash}'), 'Explorer URL template must contain a {hash} placeholder'),
149
+ });
150
+ /**
151
+ * Zod schema for validating transaction hash strings used in buildExplorerUrl.
152
+ * This schema ensures the transaction hash is a non-empty string.
153
+ */
154
+ const transactionHashSchema = z
155
+ .string({
156
+ required_error: 'Transaction hash is required',
157
+ invalid_type_error: 'Transaction hash must be a string',
158
+ })
159
+ .min(1, 'Transaction hash cannot be empty')
160
+ .transform((hash) => hash.trim()) // Automatically trim whitespace
161
+ .refine((hash) => hash.length > 0, 'Transaction hash must not be empty or whitespace-only');
162
+ /**
163
+ * Zod schema for validating buildExplorerUrl function parameters.
164
+ * This schema validates both the chain definition and transaction hash together.
165
+ */
166
+ z.object({
167
+ chainDef: chainDefinitionSchema$1,
168
+ txHash: transactionHashSchema,
169
+ });
170
+ /**
171
+ * Zod schema for validating the generated explorer URL.
172
+ * This schema ensures the generated URL is valid.
173
+ */
174
+ z
175
+ .string()
176
+ .url('Generated explorer URL is invalid');
177
+
178
+ /**
179
+ * A type-safe event emitter for managing action-based event subscriptions.
180
+ *
181
+ * Actionable provides a strongly-typed publish/subscribe pattern for events,
182
+ * where each event (action) has its own specific payload type. Handlers can
183
+ * subscribe to specific events or use a wildcard to receive all events.
184
+ *
185
+ * @typeParam AllActions - A record mapping action names to their payload types.
186
+ *
187
+ * @example
188
+ * ```typescript
189
+ * import { Actionable } from '@circle-fin/bridge-kit/utils';
190
+ *
191
+ * // Define your action types
192
+ * type TransferActions = {
193
+ * started: { txHash: string; amount: string };
194
+ * completed: { txHash: string; destinationTxHash: string };
195
+ * failed: { error: Error };
196
+ * };
197
+ *
198
+ * // Create an actionable instance
199
+ * const transferEvents = new Actionable<TransferActions>();
200
+ *
201
+ * // Subscribe to a specific event
202
+ * transferEvents.on('completed', (payload) => {
203
+ * console.log(`Transfer completed with hash: ${payload.destinationTxHash}`);
204
+ * });
205
+ *
206
+ * // Subscribe to all events
207
+ * transferEvents.on('*', (payload) => {
208
+ * console.log('Event received:', payload);
209
+ * });
210
+ *
211
+ * // Dispatch an event
212
+ * transferEvents.dispatch('completed', {
213
+ * txHash: '0x123',
214
+ * destinationTxHash: '0xabc'
215
+ * });
216
+ * ```
217
+ */
218
+ class Actionable {
219
+ // Store event handlers by action key
220
+ handlers = {};
221
+ // Store wildcard handlers that receive all events
222
+ wildcard = [];
223
+ // Implementation that handles both overloads
224
+ on(action, handler) {
225
+ if (action === '*') {
226
+ // Add to wildcard handlers array
227
+ this.wildcard.push(handler);
228
+ }
229
+ else {
230
+ // Initialize the action's handler array if it doesn't exist
231
+ if (!this.handlers[action]) {
232
+ this.handlers[action] = [];
233
+ }
234
+ // Add the handler to the specific action's array
235
+ this.handlers[action].push(handler);
236
+ }
237
+ }
238
+ // Implementation that handles both overloads
239
+ off(action, handler) {
240
+ if (action === '*') {
241
+ // Find and remove the handler from wildcard array
242
+ const index = this.wildcard.indexOf(handler);
243
+ if (index !== -1) {
244
+ this.wildcard.splice(index, 1);
245
+ }
246
+ }
247
+ else if (this.handlers[action]) {
248
+ // Check if there are handlers for this action
249
+ // Find and remove the specific handler
250
+ const index = this.handlers[action].indexOf(handler);
251
+ if (index !== -1) {
252
+ this.handlers[action].splice(index, 1);
253
+ }
254
+ }
255
+ }
256
+ /**
257
+ * Dispatch an action with its payload to all registered handlers.
258
+ *
259
+ * This method notifies both:
260
+ * - Handlers registered specifically for this action
261
+ * - Wildcard handlers registered for all actions
262
+ *
263
+ * @param action - The action key identifying the event type.
264
+ * @param payload - The data associated with the action.
265
+ *
266
+ * @example
267
+ * ```typescript
268
+ * type Actions = {
269
+ * transferStarted: { amount: string; destination: string };
270
+ * transferComplete: { txHash: string };
271
+ * };
272
+ *
273
+ * const events = new Actionable<Actions>();
274
+ *
275
+ * // Dispatch an event
276
+ * events.dispatch('transferStarted', {
277
+ * amount: '100',
278
+ * destination: '0xABC123'
279
+ * });
280
+ * ```
281
+ */
282
+ dispatch(action, payload) {
283
+ // Execute all handlers registered for this specific action
284
+ for (const h of this.handlers[action] ?? [])
285
+ h(payload);
286
+ // Execute all wildcard handlers
287
+ for (const h of this.wildcard)
288
+ h(payload);
289
+ }
290
+ }
291
+
292
+ /**
293
+ * Convert a human-readable decimal string to its smallest unit representation.
294
+ *
295
+ * This function converts user-friendly decimal values into the integer representation
296
+ * required by blockchain operations, where all values are stored in the smallest
297
+ * denomination. Uses the battle-tested implementation from @ethersproject/units.
298
+ *
299
+ * @param value - The decimal string to convert (e.g., "1.0")
300
+ * @param decimals - The number of decimal places for the unit conversion
301
+ * @returns The value in smallest units as a bigint (e.g., 1000000n for 1 USDC with 6 decimals)
302
+ * @throws Error if the value is not a valid decimal string
303
+ *
304
+ * @example
305
+ * ```typescript
306
+ * import { parseUnits } from '@core/utils'
307
+ *
308
+ * // Parse USDC (6 decimals)
309
+ * const usdcParsed = parseUnits('1.0', 6)
310
+ * console.log(usdcParsed) // 1000000n
311
+ *
312
+ * // Parse ETH (18 decimals)
313
+ * const ethParsed = parseUnits('1.0', 18)
314
+ * console.log(ethParsed) // 1000000000000000000n
315
+ *
316
+ * // Parse fractional amount
317
+ * const fractionalParsed = parseUnits('1.5', 6)
318
+ * console.log(fractionalParsed) // 1500000n
319
+ *
320
+ * // Parse integer (no decimal point)
321
+ * const integerParsed = parseUnits('42', 6)
322
+ * console.log(integerParsed) // 42000000n
323
+ * ```
324
+ */
325
+ const parseUnits = (value, decimals) => {
326
+ return parseUnits$1(value, decimals).toBigInt();
327
+ };
328
+
329
+ /**
330
+ * Valid recoverability values for error handling strategies.
331
+ *
332
+ * - FATAL errors are thrown immediately (invalid inputs, insufficient funds)
333
+ * - RETRYABLE errors are returned when a flow fails to start but could work later
334
+ * - RESUMABLE errors are returned when a flow fails mid-execution but can be continued
335
+ */
336
+ const RECOVERABILITY_VALUES = [
337
+ 'RETRYABLE',
338
+ 'RESUMABLE',
339
+ 'FATAL',
340
+ ];
341
+
342
+ // Create a mutable array for Zod enum validation
343
+ const RECOVERABILITY_ARRAY = [...RECOVERABILITY_VALUES];
344
+ /**
345
+ * Zod schema for validating ErrorDetails objects.
346
+ *
347
+ * This schema provides runtime validation for all ErrorDetails properties,
348
+ * ensuring type safety and proper error handling for JavaScript consumers.
349
+ *
350
+ * @example
351
+ * ```typescript
352
+ * import { errorDetailsSchema } from '@core/errors'
353
+ *
354
+ * const result = errorDetailsSchema.safeParse({
355
+ * code: 1001,
356
+ * name: 'NETWORK_MISMATCH',
357
+ * recoverability: 'FATAL',
358
+ * message: 'Source and destination networks must be different'
359
+ * })
360
+ *
361
+ * if (!result.success) {
362
+ * console.error('Validation failed:', result.error.issues)
363
+ * }
364
+ * ```
365
+ */
366
+ const errorDetailsSchema = z.object({
367
+ /** Numeric identifier following standardized ranges (1000+ for INPUT errors) */
368
+ code: z
369
+ .number()
370
+ .int('Error code must be an integer')
371
+ .min(1000, 'Error code must be within valid range (1000+)')
372
+ .max(1099, 'Error code must be within valid range (1099 max)'),
373
+ /** Human-readable ID (e.g., "NETWORK_MISMATCH") */
374
+ name: z
375
+ .string()
376
+ .min(1, 'Error name must be a non-empty string')
377
+ .regex(/^[A-Z_][A-Z0-9_]*$/, 'Error name must match pattern: ^[A-Z_][A-Z0-9_]*$'),
378
+ /** Error handling strategy */
379
+ recoverability: z.enum(RECOVERABILITY_ARRAY, {
380
+ errorMap: () => ({
381
+ message: 'Recoverability must be one of: RETRYABLE, RESUMABLE, FATAL',
382
+ }),
383
+ }),
384
+ /** User-friendly explanation with network context */
385
+ message: z
386
+ .string()
387
+ .min(1, 'Error message must be a non-empty string')
388
+ .max(500, 'Error message must be 500 characters or less'),
389
+ /** Raw error details, context, or the original error that caused this one. */
390
+ cause: z
391
+ .object({
392
+ /** Free-form error payload from underlying system */
393
+ trace: z.unknown().optional(),
394
+ })
395
+ .optional(),
396
+ });
397
+
398
+ /**
399
+ * Validates an ErrorDetails object using Zod schema.
400
+ *
401
+ * @param details - The object to validate
402
+ * @returns The validated ErrorDetails object
403
+ * @throws {TypeError} When validation fails
404
+ *
405
+ * @example
406
+ * ```typescript
407
+ * import { validateErrorDetails } from '@core/errors'
408
+ *
409
+ * try {
410
+ * const validDetails = validateErrorDetails({
411
+ * code: 1001,
412
+ * name: 'NETWORK_MISMATCH',
413
+ * recoverability: 'FATAL',
414
+ * message: 'Source and destination networks must be different'
415
+ * })
416
+ * } catch (error) {
417
+ * console.error('Validation failed:', error.message)
418
+ * }
419
+ * ```
420
+ */
421
+ function validateErrorDetails(details) {
422
+ const result = errorDetailsSchema.safeParse(details);
423
+ if (!result.success) {
424
+ const issues = result.error.issues
425
+ .map((issue) => `${issue.path.join('.')}: ${issue.message}`)
426
+ .join(', ');
427
+ throw new TypeError(`Invalid ErrorDetails: ${issues}`);
428
+ }
429
+ return result.data;
430
+ }
431
+
432
+ /**
433
+ * Structured error class for Stablecoin Kit operations.
434
+ *
435
+ * This class extends the native Error class while implementing the ErrorDetails
436
+ * interface, providing a consistent error format for programmatic handling
437
+ * across the Stablecoin Kits ecosystem. All properties are immutable to ensure
438
+ * error objects cannot be modified after creation.
439
+ *
440
+ * @example
441
+ * ```typescript
442
+ * import { KitError } from '@core/errors'
443
+ *
444
+ * const error = new KitError({
445
+ * code: 1001,
446
+ * name: 'INPUT_NETWORK_MISMATCH',
447
+ * recoverability: 'FATAL',
448
+ * message: 'Cannot bridge between mainnet and testnet'
449
+ * })
450
+ *
451
+ * if (error instanceof KitError) {
452
+ * console.log(`Error ${error.code}: ${error.name}`)
453
+ * // → "Error 1001: INPUT_NETWORK_MISMATCH"
454
+ * }
455
+ * ```
456
+ *
457
+ * @example
458
+ * ```typescript
459
+ * import { KitError } from '@core/errors'
460
+ *
461
+ * // Error with cause information
462
+ * const error = new KitError({
463
+ * code: 1002,
464
+ * name: 'INVALID_AMOUNT',
465
+ * recoverability: 'FATAL',
466
+ * message: 'Amount must be greater than zero',
467
+ * cause: {
468
+ * trace: { providedAmount: -100, minimumAmount: 0 }
469
+ * }
470
+ * })
471
+ *
472
+ * throw error
473
+ * ```
474
+ */
475
+ class KitError extends Error {
476
+ /** Numeric identifier following standardized ranges (1000+ for INPUT errors) */
477
+ code;
478
+ /** Human-readable ID (e.g., "NETWORK_MISMATCH") */
479
+ name;
480
+ /** Error handling strategy */
481
+ recoverability;
482
+ /** Raw error details, context, or the original error that caused this one. */
483
+ cause;
484
+ /**
485
+ * Create a new KitError instance.
486
+ *
487
+ * @param details - The error details object containing all required properties.
488
+ * @throws \{TypeError\} When details parameter is missing or invalid.
489
+ */
490
+ constructor(details) {
491
+ // Validate input at runtime for JavaScript consumers using Zod
492
+ const validatedDetails = validateErrorDetails(details);
493
+ super(validatedDetails.message);
494
+ // Set properties as readonly at runtime
495
+ Object.defineProperties(this, {
496
+ name: {
497
+ value: validatedDetails.name,
498
+ writable: false,
499
+ enumerable: true,
500
+ configurable: false,
501
+ },
502
+ code: {
503
+ value: validatedDetails.code,
504
+ writable: false,
505
+ enumerable: true,
506
+ configurable: false,
507
+ },
508
+ recoverability: {
509
+ value: validatedDetails.recoverability,
510
+ writable: false,
511
+ enumerable: true,
512
+ configurable: false,
513
+ },
514
+ ...(validatedDetails.cause && {
515
+ cause: {
516
+ value: validatedDetails.cause,
517
+ writable: false,
518
+ enumerable: true,
519
+ configurable: false,
520
+ },
521
+ }),
522
+ });
523
+ }
524
+ }
525
+
526
+ /**
527
+ * Standardized error definitions for INPUT type errors.
528
+ *
529
+ * Each entry combines the numeric error code with its corresponding
530
+ * string name to ensure consistency when creating error instances.
531
+ *
532
+ * Error codes follow a hierarchical numbering scheme where the first digit
533
+ * indicates the error category (1 = INPUT) and subsequent digits provide
534
+ * specific error identification within that category.
535
+ *
536
+ *
537
+ * @example
538
+ * ```typescript
539
+ * import { InputError } from '@core/errors'
540
+ *
541
+ * const error = new KitError({
542
+ * ...InputError.NETWORK_MISMATCH,
543
+ * recoverability: 'FATAL',
544
+ * message: 'Source and destination networks must be different'
545
+ * })
546
+ *
547
+ * // Access code and name individually if needed
548
+ * console.log(InputError.NETWORK_MISMATCH.code) // 1001
549
+ * console.log(InputError.NETWORK_MISMATCH.name) // 'INPUT_NETWORK_MISMATCH'
550
+ * ```
551
+ */
552
+ const InputError = {
553
+ /** Network type mismatch between chains (mainnet vs testnet) */
554
+ NETWORK_MISMATCH: {
555
+ code: 1001,
556
+ name: 'INPUT_NETWORK_MISMATCH',
557
+ },
558
+ /** Invalid amount format or value (negative, zero, or malformed) */
559
+ INVALID_AMOUNT: {
560
+ code: 1002,
561
+ name: 'INPUT_INVALID_AMOUNT',
562
+ },
563
+ /** Unsupported or invalid bridge route configuration */
564
+ UNSUPPORTED_ROUTE: {
565
+ code: 1003,
566
+ name: 'INPUT_UNSUPPORTED_ROUTE',
567
+ },
568
+ /** Invalid wallet or contract address format */
569
+ INVALID_ADDRESS: {
570
+ code: 1004,
571
+ name: 'INPUT_INVALID_ADDRESS',
572
+ },
573
+ /** Invalid or unsupported chain identifier */
574
+ INVALID_CHAIN: {
575
+ code: 1005,
576
+ name: 'INPUT_INVALID_CHAIN',
577
+ },
578
+ /** General validation failure for complex validation rules */
579
+ VALIDATION_FAILED: {
580
+ code: 1098,
581
+ name: 'INPUT_VALIDATION_FAILED',
582
+ },
583
+ };
584
+
585
+ /**
586
+ * Creates error for network type mismatch between source and destination.
587
+ *
588
+ * This error is thrown when attempting to bridge between chains that have
589
+ * different network types (e.g., mainnet to testnet), which is not supported
590
+ * for security reasons.
591
+ *
592
+ * @param sourceChain - The source chain definition
593
+ * @param destChain - The destination chain definition
594
+ * @returns KitError with specific network mismatch details
595
+ *
596
+ * @example
597
+ * ```typescript
598
+ * import { createNetworkMismatchError } from '@core/errors'
599
+ * import { Ethereum, BaseSepolia } from '@core/chains'
600
+ *
601
+ * // This will throw a detailed error
602
+ * throw createNetworkMismatchError(Ethereum, BaseSepolia)
603
+ * // Message: "Cannot bridge between Ethereum (mainnet) and Base Sepolia (testnet). Source and destination networks must both be testnet or both be mainnet."
604
+ * ```
605
+ */
606
+ function createNetworkMismatchError(sourceChain, destChain) {
607
+ const sourceNetworkType = sourceChain.isTestnet ? 'testnet' : 'mainnet';
608
+ const destNetworkType = destChain.isTestnet ? 'testnet' : 'mainnet';
609
+ const errorDetails = {
610
+ ...InputError.NETWORK_MISMATCH,
611
+ recoverability: 'FATAL',
612
+ message: `Cannot bridge between ${sourceChain.name} (${sourceNetworkType}) and ${destChain.name} (${destNetworkType}). Source and destination networks must both be testnet or both be mainnet.`,
613
+ cause: {
614
+ trace: { sourceChain: sourceChain.name, destChain: destChain.name },
615
+ },
616
+ };
617
+ return new KitError(errorDetails);
618
+ }
619
+ /**
620
+ * Creates error for unsupported bridge route.
621
+ *
622
+ * This error is thrown when attempting to bridge between chains that don't
623
+ * have a supported bridge route configured.
624
+ *
625
+ * @param source - Source chain name
626
+ * @param destination - Destination chain name
627
+ * @returns KitError with specific route details
628
+ *
629
+ * @example
630
+ * ```typescript
631
+ * import { createUnsupportedRouteError } from '@core/errors'
632
+ *
633
+ * throw createUnsupportedRouteError('Ethereum', 'Solana')
634
+ * // Message: "Route from Ethereum to Solana is not supported"
635
+ * ```
636
+ */
637
+ function createUnsupportedRouteError(source, destination) {
638
+ const errorDetails = {
639
+ ...InputError.UNSUPPORTED_ROUTE,
640
+ recoverability: 'FATAL',
641
+ message: `Route from ${source} to ${destination} is not supported.`,
642
+ cause: {
643
+ trace: { source, destination },
644
+ },
645
+ };
646
+ return new KitError(errorDetails);
647
+ }
648
+ /**
649
+ * Creates error for invalid amount format or precision.
650
+ *
651
+ * This error is thrown when the provided amount doesn't meet validation
652
+ * requirements such as precision, range, or format.
653
+ *
654
+ * @param amount - The invalid amount string
655
+ * @param reason - Specific reason why amount is invalid
656
+ * @returns KitError with amount details and validation rule
657
+ *
658
+ * @example
659
+ * ```typescript
660
+ * import { createInvalidAmountError } from '@core/errors'
661
+ *
662
+ * throw createInvalidAmountError('0.000001', 'Amount must be at least 0.01 USDC')
663
+ * // Message: "Invalid amount '0.000001': Amount must be at least 0.01 USDC"
664
+ *
665
+ * throw createInvalidAmountError('abc', 'Amount must be a valid number')
666
+ * // Message: "Invalid amount 'abc': Amount must be a valid number"
667
+ * ```
668
+ */
669
+ function createInvalidAmountError(amount, reason) {
670
+ const errorDetails = {
671
+ ...InputError.INVALID_AMOUNT,
672
+ recoverability: 'FATAL',
673
+ message: `Invalid amount '${amount}': ${reason}.`,
674
+ cause: {
675
+ trace: { amount, reason },
676
+ },
677
+ };
678
+ return new KitError(errorDetails);
679
+ }
680
+ /**
681
+ * Creates error for invalid wallet address format.
682
+ *
683
+ * This error is thrown when the provided address doesn't match the expected
684
+ * format for the specified chain.
685
+ *
686
+ * @param address - The invalid address string
687
+ * @param chain - Chain name where address is invalid
688
+ * @param expectedFormat - Description of expected address format
689
+ * @returns KitError with address details and format requirements
690
+ *
691
+ * @example
692
+ * ```typescript
693
+ * import { createInvalidAddressError } from '@core/errors'
694
+ *
695
+ * throw createInvalidAddressError('0x123', 'Ethereum', '42-character hex string starting with 0x')
696
+ * // Message: "Invalid address '0x123' for Ethereum. Expected 42-character hex string starting with 0x."
697
+ *
698
+ * throw createInvalidAddressError('invalid', 'Solana', 'base58-encoded string')
699
+ * // Message: "Invalid address 'invalid' for Solana. Expected base58-encoded string."
700
+ * ```
701
+ */
702
+ function createInvalidAddressError(address, chain, expectedFormat) {
703
+ const errorDetails = {
704
+ ...InputError.INVALID_ADDRESS,
705
+ recoverability: 'FATAL',
706
+ message: `Invalid address '${address}' for ${chain}. Expected ${expectedFormat}.`,
707
+ cause: {
708
+ trace: { address, chain, expectedFormat },
709
+ },
710
+ };
711
+ return new KitError(errorDetails);
712
+ }
713
+ /**
714
+ * Creates error for invalid chain configuration.
715
+ *
716
+ * This error is thrown when the provided chain doesn't meet the required
717
+ * configuration or is not supported for the operation.
718
+ *
719
+ * @param chain - The invalid chain name or identifier
720
+ * @param reason - Specific reason why chain is invalid
721
+ * @returns KitError with chain details and validation rule
722
+ *
723
+ * @example
724
+ * ```typescript
725
+ * import { createInvalidChainError } from '@core/errors'
726
+ *
727
+ * throw createInvalidChainError('UnknownChain', 'Chain is not supported by this bridge')
728
+ * // Message: "Invalid chain 'UnknownChain': Chain is not supported by this bridge"
729
+ * ```
730
+ */
731
+ function createInvalidChainError(chain, reason) {
732
+ const errorDetails = {
733
+ ...InputError.INVALID_CHAIN,
734
+ recoverability: 'FATAL',
735
+ message: `Invalid chain '${chain}': ${reason}`,
736
+ cause: {
737
+ trace: { chain, reason },
738
+ },
739
+ };
740
+ return new KitError(errorDetails);
741
+ }
742
+
743
+ /**
744
+ * Extracts chain information including name, display name, and expected address format.
745
+ *
746
+ * This function determines the chain type by checking the explicit `type` property first,
747
+ * then falls back to name-based matching. This approach is more robust than relying solely
748
+ * on string matching and future-proofs the code for new chain ecosystems.
749
+ *
750
+ * @param chain - The chain identifier (string, chain object, or undefined/null)
751
+ * @returns Chain information with name, display name, and expected address format
752
+ *
753
+ * @example
754
+ * ```typescript
755
+ * import { extractChainInfo } from '@core/errors'
756
+ *
757
+ * // With chain object
758
+ * const info1 = extractChainInfo({ name: 'Ethereum', type: 'evm' })
759
+ * console.log(info1.expectedAddressFormat)
760
+ * // → "42-character hex address starting with 0x"
761
+ *
762
+ * // With string
763
+ * const info2 = extractChainInfo('Solana')
764
+ * console.log(info2.expectedAddressFormat)
765
+ * // → "44-character base58 encoded string"
766
+ *
767
+ * // With undefined
768
+ * const info3 = extractChainInfo(undefined)
769
+ * console.log(info3.name) // → "unknown"
770
+ * ```
771
+ */
772
+ function extractChainInfo(chain) {
773
+ const name = typeof chain === 'string' ? chain : (chain?.name ?? 'unknown');
774
+ const chainType = typeof chain === 'object' && chain !== null ? chain.type : undefined;
775
+ // Use explicit chain type if available, fallback to name matching
776
+ const isSolana = chainType !== undefined
777
+ ? chainType === 'solana'
778
+ : name.toLowerCase().includes('solana');
779
+ return {
780
+ name,
781
+ displayName: name.replace(/_/g, ' '),
782
+ expectedAddressFormat: isSolana
783
+ ? '44-character base58 encoded string'
784
+ : '42-character hex address starting with 0x',
785
+ };
786
+ }
787
+ /**
788
+ * Validates if an address format is correct for the specified chain.
789
+ *
790
+ * This function checks the explicit chain `type` property first, then falls back
791
+ * to name-based matching to determine whether to validate as a Solana or EVM address.
792
+ *
793
+ * @param address - The address to validate
794
+ * @param chain - The chain identifier or chain definition (cannot be null)
795
+ * @returns True if the address format is valid for the chain, false otherwise
796
+ *
797
+ * @example
798
+ * ```typescript
799
+ * import { isValidAddressForChain } from '@core/errors'
800
+ *
801
+ * // EVM address validation
802
+ * isValidAddressForChain('0x742d35Cc6634C0532925a3b8D0C0C1C4C5C6C7C8', 'Ethereum')
803
+ * // → true
804
+ *
805
+ * // Solana address validation
806
+ * isValidAddressForChain('9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM', { name: 'Solana', type: 'solana' })
807
+ * // → true
808
+ *
809
+ * // Invalid format
810
+ * isValidAddressForChain('invalid', 'Ethereum')
811
+ * // → false
812
+ * ```
813
+ */
814
+ function isValidAddressForChain(address, chain) {
815
+ const chainType = typeof chain === 'object' ? chain.type : undefined;
816
+ const name = typeof chain === 'string' ? chain : chain.name;
817
+ // Use explicit chain type if available, fallback to name matching
818
+ const isSolana = chainType !== undefined
819
+ ? chainType === 'solana'
820
+ : name.toLowerCase().includes('solana');
821
+ if (isSolana) {
822
+ // Solana base58 address: 32-44 characters from base58 alphabet
823
+ return /^[1-9A-HJ-NP-Za-km-z]{32,44}$/.test(address);
824
+ }
825
+ // EVM hex address: 0x followed by 40 hex characters
826
+ return /^0x[a-fA-F0-9]{40}$/.test(address);
827
+ }
828
+ /**
829
+ * Type guard to check if a value is an object.
830
+ *
831
+ * @param val - The value to check
832
+ * @returns True if the value is a non-null object
833
+ */
834
+ function isObject(val) {
835
+ return typeof val === 'object' && val !== null;
836
+ }
837
+ /**
838
+ * Type guard to check if a value is a chain with isTestnet property.
839
+ *
840
+ * @param chain - The value to check
841
+ * @returns True if the value is a chain object with name and isTestnet properties
842
+ *
843
+ * @example
844
+ * ```typescript
845
+ * import { isChainWithTestnet } from '@core/errors'
846
+ *
847
+ * const chain1 = { name: 'Ethereum', isTestnet: false }
848
+ * isChainWithTestnet(chain1) // → true
849
+ *
850
+ * const chain2 = { name: 'Ethereum' }
851
+ * isChainWithTestnet(chain2) // → false
852
+ *
853
+ * const chain3 = 'Ethereum'
854
+ * isChainWithTestnet(chain3) // → false
855
+ * ```
856
+ */
857
+ function isChainWithTestnet(chain) {
858
+ return (isObject(chain) &&
859
+ 'isTestnet' in chain &&
860
+ typeof chain['isTestnet'] === 'boolean' &&
861
+ 'name' in chain &&
862
+ typeof chain['name'] === 'string');
863
+ }
864
+ /**
865
+ * Gets chain identifier from toData object.
866
+ *
867
+ * Looks for chain in context.chain first, then falls back to direct chain property.
868
+ *
869
+ * @param toData - The destination data object
870
+ * @returns The chain identifier or null
871
+ */
872
+ function getChainFromToData(toData) {
873
+ const context = toData['context'];
874
+ const chain = context?.['chain'] ?? toData['chain'];
875
+ return chain;
876
+ }
877
+ /**
878
+ * Gets chain name from params object.
879
+ *
880
+ * @param params - The parameters object
881
+ * @returns The chain name as a string, or null if not found
882
+ */
883
+ function getChainName(params) {
884
+ if (isObject(params)) {
885
+ const chain = params['chain'];
886
+ if (typeof chain === 'string') {
887
+ return chain;
888
+ }
889
+ if (isObject(chain) && 'name' in chain) {
890
+ return chain.name;
891
+ }
892
+ }
893
+ return null;
894
+ }
895
+ /**
896
+ * Extracts from data from params object.
897
+ *
898
+ * Supports both 'from' and 'source' property names.
899
+ *
900
+ * @param params - The parameters object
901
+ * @returns The from/source data or undefined
902
+ */
903
+ function extractFromData(params) {
904
+ return (params['from'] ?? params['source']);
905
+ }
906
+ /**
907
+ * Extracts to data from params object.
908
+ *
909
+ * Supports both 'to' and 'destination' property names.
910
+ *
911
+ * @param params - The parameters object
912
+ * @returns The to/destination data or undefined
913
+ */
914
+ function extractToData(params) {
915
+ return (params['to'] ?? params['destination']);
916
+ }
917
+ /**
918
+ * Gets address from params object using a dot-separated path.
919
+ *
920
+ * Traverses nested objects to extract the address value at the specified path.
921
+ *
922
+ * @param params - The parameters object
923
+ * @param path - Dot-separated path to extract address from (e.g., 'to.recipientAddress')
924
+ * @returns The address as a string, or 'unknown' if not found
925
+ *
926
+ * @example
927
+ * ```typescript
928
+ * // Extract from specific path
929
+ * getAddressFromParams(params, 'to.recipientAddress')
930
+ * // Returns the value at params.to.recipientAddress
931
+ * ```
932
+ */
933
+ function getAddressFromParams(params, path) {
934
+ const parts = path.split('.');
935
+ let current = params;
936
+ for (const part of parts) {
937
+ if (current !== null &&
938
+ current !== undefined &&
939
+ typeof current === 'object' &&
940
+ part in current) {
941
+ current = current[part];
942
+ }
943
+ else {
944
+ return 'unknown';
945
+ }
946
+ }
947
+ return typeof current === 'string' ? current : 'unknown';
948
+ }
949
+ /**
950
+ * Gets chain identifier from params object.
951
+ *
952
+ * Looks for chain in to.context.chain, to.chain, or direct chain property.
953
+ *
954
+ * @param params - The parameters object
955
+ * @returns The chain identifier or null
956
+ */
957
+ function getChainFromParams(params) {
958
+ const to = extractToData(params);
959
+ const chain = to?.['chain'] ?? params['chain'];
960
+ return chain;
961
+ }
962
+
963
+ /**
964
+ * Maximum length for error messages in fallback validation errors.
965
+ *
966
+ * KitError enforces a 500-character limit on error messages. When creating
967
+ * fallback validation errors that combine multiple Zod issues, we use 450
968
+ * characters to leave a 50-character buffer for:
969
+ * - The error message prefix ("Invalid bridge parameters: ")
970
+ * - Potential encoding differences or formatting overhead
971
+ * - Safety margin to prevent KitError constructor failures
972
+ *
973
+ * This ensures that even with concatenated issue summaries, the final message
974
+ * stays within KitError's constraints.
975
+ */
976
+ const MAX_MESSAGE_LENGTH = 450;
977
+
978
+ /**
979
+ * Converts a Zod validation error into a specific KitError instance using structured pattern matching.
980
+ *
981
+ * This function inspects Zod's error details (path, code, message) and delegates each issue
982
+ * to specialized handlers that generate domain-specific KitError objects. It leverages
983
+ * Zod's error codes and path information for robust matching, avoiding fragile string checks.
984
+ *
985
+ * @param zodError - The Zod validation error containing one or more issues
986
+ * @param params - The original parameters that failed validation (used to extract invalid values)
987
+ * @returns A specific KitError instance with actionable error details
988
+ */
989
+ function convertZodErrorToStructured(zodError, params) {
990
+ // Handle null/undefined params gracefully
991
+ if (params === null || params === undefined) {
992
+ return createValidationFailedError(zodError);
993
+ }
994
+ const paramsObj = params;
995
+ const toData = extractToData(paramsObj);
996
+ const fromData = extractFromData(paramsObj);
997
+ for (const issue of zodError.issues) {
998
+ const path = issue.path.join('.');
999
+ const code = issue.code;
1000
+ // Try to handle specific error types
1001
+ const amountError = handleAmountError(path, code, issue.message, paramsObj);
1002
+ if (amountError)
1003
+ return amountError;
1004
+ const chainError = handleChainError(path, code, issue.message, fromData, toData);
1005
+ if (chainError)
1006
+ return chainError;
1007
+ const addressError = handleAddressError(path, code, issue.message, paramsObj);
1008
+ if (addressError)
1009
+ return addressError;
1010
+ }
1011
+ // Fallback
1012
+ return createValidationFailedError(zodError);
1013
+ }
1014
+ /**
1015
+ * Creates a generic validation failed error for null/undefined params or unmapped error cases.
1016
+ *
1017
+ * This is a fallback handler used when:
1018
+ * - Parameters are null or undefined
1019
+ * - No specific error handler matches the Zod error
1020
+ * - The error doesn't fit into amount/chain/address categories
1021
+ *
1022
+ * The function truncates the error message to stay within KitError's 500-character limit.
1023
+ *
1024
+ * @param zodError - The Zod validation error with all issues
1025
+ * @param params - The original parameters (may be null/undefined)
1026
+ * @returns A generic KitError with INPUT_VALIDATION_FAILED code
1027
+ */
1028
+ function createValidationFailedError(zodError) {
1029
+ const issueSummary = zodError.issues
1030
+ .map((issue) => `${issue.path.join('.')}: ${issue.message}`)
1031
+ .join('; ');
1032
+ // Truncate message to avoid KitError constructor failure.
1033
+ const fullMessage = `Invalid parameters: ${issueSummary}`;
1034
+ const truncatedMessage = fullMessage.length > MAX_MESSAGE_LENGTH
1035
+ ? `${fullMessage.substring(0, MAX_MESSAGE_LENGTH)}...`
1036
+ : fullMessage;
1037
+ return new KitError({
1038
+ ...InputError.VALIDATION_FAILED,
1039
+ recoverability: 'FATAL',
1040
+ message: truncatedMessage,
1041
+ cause: {
1042
+ trace: {
1043
+ originalError: zodError.message,
1044
+ zodIssues: zodError.issues,
1045
+ },
1046
+ },
1047
+ });
1048
+ }
1049
+ /**
1050
+ * Handles amount-related validation errors from Zod.
1051
+ *
1052
+ * Checks if the validation error path includes 'amount' and attempts
1053
+ * to convert generic Zod errors into specific KitError instances with
1054
+ * actionable messages. Delegates to specialized handlers in order of specificity:
1055
+ * 1. Negative amount errors (too_small)
1056
+ * 2. Custom validation errors (decimal places, numeric string)
1057
+ * 3. Invalid string format errors
1058
+ * 4. Invalid type errors
1059
+ *
1060
+ * @param path - The Zod error path (e.g., 'amount' or 'config.amount')
1061
+ * @param code - The Zod error code (e.g., 'too_small', 'invalid_string', 'custom')
1062
+ * @param message - The original Zod error message
1063
+ * @param paramsObj - The original params object for extracting the invalid amount value
1064
+ * @returns KitError with INPUT_INVALID_AMOUNT code if this is an amount error, null otherwise
1065
+ */
1066
+ function handleAmountError(path, code, message, paramsObj) {
1067
+ if (!path.includes('amount'))
1068
+ return null;
1069
+ const amount = typeof paramsObj['amount'] === 'string' ? paramsObj['amount'] : 'unknown';
1070
+ // Try different error handlers in order of specificity
1071
+ const negativeError = handleNegativeAmountError(code, message, amount);
1072
+ if (negativeError)
1073
+ return negativeError;
1074
+ const customError = handleCustomAmountError(code, message, amount);
1075
+ if (customError)
1076
+ return customError;
1077
+ const stringFormatError = handleInvalidStringAmountError(code, message, amount);
1078
+ if (stringFormatError)
1079
+ return stringFormatError;
1080
+ const typeError = handleInvalidTypeAmountError(code, amount);
1081
+ if (typeError)
1082
+ return typeError;
1083
+ return null;
1084
+ }
1085
+ /**
1086
+ * Handles negative or too-small amount validation errors.
1087
+ *
1088
+ * Detects Zod 'too_small' error codes or messages containing 'greater than'
1089
+ * and creates a specific error indicating the amount must be positive.
1090
+ *
1091
+ * @param code - The Zod error code
1092
+ * @param message - The Zod error message
1093
+ * @param amount - The invalid amount value as a string
1094
+ * @returns KitError if this is a negative/too-small amount error, null otherwise
1095
+ */
1096
+ function handleNegativeAmountError(code, message, amount) {
1097
+ if (code === 'too_small' || message.includes('greater than')) {
1098
+ return createInvalidAmountError(amount, 'Amount must be greater than 0');
1099
+ }
1100
+ return null;
1101
+ }
1102
+ /**
1103
+ * Handles custom Zod refinement validation errors for amounts.
1104
+ *
1105
+ * Processes Zod 'custom' error codes from .refine() validators and matches
1106
+ * against known patterns:
1107
+ * - 'non-negative' - amount must be \>= 0
1108
+ * - 'decimal places' - too many decimal places
1109
+ * - 'numeric string' - value is not a valid numeric string
1110
+ *
1111
+ * @param code - The Zod error code (must be 'custom')
1112
+ * @param message - The custom error message from the refinement
1113
+ * @param amount - The invalid amount value as a string
1114
+ * @returns KitError with specific message if pattern matches, null otherwise
1115
+ */
1116
+ function handleCustomAmountError(code, message, amount) {
1117
+ if (code !== 'custom')
1118
+ return null;
1119
+ if (message.includes('non-negative')) {
1120
+ return createInvalidAmountError(amount, 'Amount must be non-negative');
1121
+ }
1122
+ if (message.includes('decimal places')) {
1123
+ return createInvalidAmountError(amount, message);
1124
+ }
1125
+ if (message.includes('numeric string')) {
1126
+ return createInvalidAmountError(amount, 'Amount must be a numeric string');
1127
+ }
1128
+ return null;
1129
+ }
1130
+ /**
1131
+ * Handles Zod 'invalid_string' errors for amount values.
1132
+ *
1133
+ * Processes string validation failures (e.g., regex mismatches) and categorizes them:
1134
+ * 1. Negative numbers that pass Number() but fail other validations
1135
+ * 2. Decimal places validation failures (too many decimal places)
1136
+ * 3. Numeric format validation failures (contains non-numeric characters)
1137
+ * 4. Generic invalid number strings
1138
+ *
1139
+ * @param code - The Zod error code (must be 'invalid_string')
1140
+ * @param message - The Zod error message from string validation
1141
+ * @param amount - The invalid amount value as a string
1142
+ * @returns KitError with context-specific message if pattern matches, null otherwise
1143
+ */
1144
+ function handleInvalidStringAmountError(code, message, amount) {
1145
+ if (code !== 'invalid_string')
1146
+ return null;
1147
+ // Check if it's a negative number first
1148
+ if (amount.startsWith('-') && !isNaN(Number(amount))) {
1149
+ return createInvalidAmountError(amount, 'Amount must be greater than 0');
1150
+ }
1151
+ // Check for decimal places validation
1152
+ if (isDecimalPlacesError(message)) {
1153
+ return createInvalidAmountError(amount, 'Maximum supported decimal places: 6');
1154
+ }
1155
+ // Check for numeric format validation
1156
+ if (isNumericFormatError(message)) {
1157
+ return createInvalidAmountError(amount, 'Amount must be a valid number format');
1158
+ }
1159
+ // For other cases like 'abc', return specific error
1160
+ if (!message.includes('valid number format')) {
1161
+ return createInvalidAmountError(amount, 'Amount must be a valid number string');
1162
+ }
1163
+ return null;
1164
+ }
1165
+ /**
1166
+ * Handles Zod 'invalid_type' errors for amount values.
1167
+ *
1168
+ * Triggered when the amount is not a string type (e.g., number, boolean, object).
1169
+ * Creates an error indicating the amount must be a string representation of a number.
1170
+ *
1171
+ * @param code - The Zod error code (must be 'invalid_type')
1172
+ * @param amount - The invalid amount value (will be converted to string for error message)
1173
+ * @returns KitError if this is a type mismatch, null otherwise
1174
+ */
1175
+ function handleInvalidTypeAmountError(code, amount) {
1176
+ if (code === 'invalid_type') {
1177
+ return createInvalidAmountError(amount, 'Amount must be a valid number string');
1178
+ }
1179
+ return null;
1180
+ }
1181
+ /**
1182
+ * Checks if an error message indicates a decimal places validation failure.
1183
+ *
1184
+ * Looks for keywords like 'maximum', 'at most', and 'decimal places' to identify
1185
+ * errors related to too many decimal digits in an amount value.
1186
+ *
1187
+ * @param message - The error message to analyze
1188
+ * @returns True if the message indicates a decimal places error, false otherwise
1189
+ */
1190
+ function isDecimalPlacesError(message) {
1191
+ return ((message.includes('maximum') || message.includes('at most')) &&
1192
+ message.includes('decimal places'));
1193
+ }
1194
+ /**
1195
+ * Checks if an error message indicates a numeric format validation failure.
1196
+ *
1197
+ * Identifies errors where a value contains 'numeric' but is not specifically
1198
+ * about 'valid number format' or 'numeric string' (to avoid false positives).
1199
+ *
1200
+ * @param message - The error message to analyze
1201
+ * @returns True if the message indicates a numeric format error, false otherwise
1202
+ */
1203
+ function isNumericFormatError(message) {
1204
+ return (message.includes('numeric') &&
1205
+ !message.includes('valid number format') &&
1206
+ !message.includes('numeric string'));
1207
+ }
1208
+ /**
1209
+ * Handles chain-related validation errors from Zod.
1210
+ *
1211
+ * Checks if the validation error path includes 'chain' and extracts the
1212
+ * chain name from either the source (from) or destination (to) data.
1213
+ * Creates a KitError with the invalid chain name and original error message.
1214
+ *
1215
+ * @param path - The Zod error path (e.g., 'from.chain' or 'to.chain')
1216
+ * @param _code - The Zod error code (unused, prefixed with _ to indicate intentionally ignored)
1217
+ * @param message - The original Zod error message
1218
+ * @param fromData - The source/from data object (may contain chain info)
1219
+ * @param toData - The destination/to data object (may contain chain info)
1220
+ * @returns KitError with INPUT_INVALID_CHAIN code if this is a chain error, null otherwise
1221
+ */
1222
+ function handleChainError(path, _code, message, fromData, toData) {
1223
+ if (!path.includes('chain'))
1224
+ return null;
1225
+ const chain = getChainName(fromData) ?? getChainName(toData);
1226
+ return createInvalidChainError(chain ?? 'unknown', message);
1227
+ }
1228
+ /**
1229
+ * Handles address-related validation errors from Zod.
1230
+ *
1231
+ * Checks if the validation error path includes 'address' and extracts both
1232
+ * the invalid address from the path and the target chain from the params.
1233
+ * Uses chain utilities to determine the expected address format (EVM or Solana)
1234
+ * and creates a context-specific error message.
1235
+ *
1236
+ * @param path - The Zod error path (e.g., 'to.recipientAddress')
1237
+ * @param _code - The Zod error code (unused, prefixed with _ to indicate intentionally ignored)
1238
+ * @param _message - The original Zod error message (unused, we create a more specific message)
1239
+ * @param paramsObj - The original params object for extracting address and chain info
1240
+ * @returns KitError with INPUT_INVALID_ADDRESS code if this is an address error, null otherwise
1241
+ */
1242
+ function handleAddressError(path, _code, _message, paramsObj) {
1243
+ if (!path.toLowerCase().includes('address'))
1244
+ return null;
1245
+ const address = getAddressFromParams(paramsObj, path);
1246
+ const chain = getChainFromParams(paramsObj);
1247
+ const chainInfo = extractChainInfo(chain);
1248
+ return createInvalidAddressError(address, chainInfo.displayName, chainInfo.expectedAddressFormat);
1249
+ }
1250
+
1251
+ const assertCustomFeePolicySymbol = Symbol('assertCustomFeePolicy');
1252
+ /**
1253
+ * Schema for validating BridgeKit custom fee policy.
1254
+ *
1255
+ * Validates the shape of {@link CustomFeePolicy}, which lets SDK consumers
1256
+ * provide custom fee calculation and fee-recipient resolution logic.
1257
+ *
1258
+ * - calculateFee: required function that returns a fee as a string (or Promise<string>).
1259
+ * - resolveFeeRecipientAddress: required function that returns a recipient address as a
1260
+ * string (or Promise<string>).
1261
+ *
1262
+ * This schema only ensures the presence and return types of the functions; it
1263
+ * does not validate their argument types.
1264
+ *
1265
+ * @example
1266
+ * ```ts
1267
+ * const config = {
1268
+ * calculateFee: async () => '1000000',
1269
+ * resolveFeeRecipientAddress: () => '0x1234567890123456789012345678901234567890',
1270
+ * }
1271
+ * const result = customFeePolicySchema.safeParse(config)
1272
+ * // result.success === true
1273
+ * ```
1274
+ */
1275
+ const customFeePolicySchema = z
1276
+ .object({
1277
+ calculateFee: z.function().returns(z.string().or(z.promise(z.string()))),
1278
+ resolveFeeRecipientAddress: z
1279
+ .function()
1280
+ .returns(z.string().or(z.promise(z.string()))),
1281
+ })
1282
+ .strict();
1283
+ /**
1284
+ * Assert that the provided value conforms to {@link CustomFeePolicy}.
1285
+ *
1286
+ * Throws a validation error with annotated paths if the configuration is
1287
+ * malformed.
1288
+ *
1289
+ * @param config - The custom fee policy to validate
1290
+ *
1291
+ * @example
1292
+ * ```ts
1293
+ * const config = {
1294
+ * calculateFee: () => '1000000',
1295
+ * resolveFeeRecipientAddress: () => '0x1234567890123456789012345678901234567890',
1296
+ * }
1297
+ * assertCustomFeePolicy(config)
1298
+ * // If no error is thrown, `config` is a valid CustomFeePolicy
1299
+ * ```
1300
+ */
1301
+ function assertCustomFeePolicy(config) {
1302
+ validateWithStateTracking(config, customFeePolicySchema, 'BridgeKit custom fee policy', assertCustomFeePolicySymbol);
1303
+ }
1304
+
1305
+ /**
1306
+ * Symbol used to track that assertBridgeParams has validated an object.
1307
+ * @internal
1308
+ */
1309
+ const ASSERT_BRIDGE_PARAMS_SYMBOL = Symbol('assertBridgeParams');
1310
+ /**
1311
+ * Validates that source and destination chains are both testnet or both mainnet.
1312
+ * @param fromData - Source chain data
1313
+ * @param toData - Destination chain data
1314
+ * @throws KitError If networks are mismatched
1315
+ * @internal
1316
+ */
1317
+ function validateNetworkMatch(fromData, toData) {
1318
+ if (!isChainWithTestnet(fromData['chain']) ||
1319
+ !isChainWithTestnet(toData['chain']) ||
1320
+ fromData['chain'].isTestnet === toData['chain'].isTestnet) {
1321
+ return;
1322
+ }
1323
+ throw new KitError({
1324
+ ...InputError.NETWORK_MISMATCH,
1325
+ recoverability: 'FATAL',
1326
+ message: `Cannot bridge between ${fromData['chain'].name} (${fromData['chain'].isTestnet ? 'testnet' : 'mainnet'}) and ${toData['chain'].name} (${toData['chain'].isTestnet ? 'testnet' : 'mainnet'}). Source and destination networks must both be testnet or both be mainnet.`,
1327
+ cause: {
1328
+ trace: {
1329
+ fromChain: fromData['chain'].name,
1330
+ toChain: toData['chain'].name,
1331
+ fromIsTestnet: fromData['chain'].isTestnet,
1332
+ toIsTestnet: toData['chain'].isTestnet,
1333
+ },
1334
+ },
1335
+ });
1336
+ }
1337
+ /**
1338
+ * Validates that the address format matches the destination chain requirements.
1339
+ * @param toData - Destination chain data containing address
1340
+ * @throws KitError If address format is invalid for the chain
1341
+ * @internal
1342
+ */
1343
+ function validateAddressFormat(toData) {
1344
+ if (typeof toData['address'] !== 'string') {
1345
+ return;
1346
+ }
1347
+ const address = toData['address'];
1348
+ const chain = getChainFromToData(toData);
1349
+ if (chain !== null && !isValidAddressForChain(address, chain)) {
1350
+ const chainInfo = extractChainInfo(chain);
1351
+ throw createInvalidAddressError(address, chainInfo.displayName, chainInfo.expectedAddressFormat);
1352
+ }
1353
+ }
1354
+ function assertBridgeParams(params, schema) {
1355
+ // Use validateWithStateTracking to avoid duplicate validations
1356
+ // This will skip validation if already validated by this function
1357
+ try {
1358
+ validateWithStateTracking(params, schema, 'bridge parameters', ASSERT_BRIDGE_PARAMS_SYMBOL);
1359
+ }
1360
+ catch (error) {
1361
+ // Convert ValidationError to structured KitError
1362
+ if (error instanceof ValidationError) {
1363
+ // Extract the underlying Zod error from ValidationError
1364
+ // ValidationError wraps the Zod validation failure
1365
+ const result = schema.safeParse(params);
1366
+ if (!result.success) {
1367
+ throw convertZodErrorToStructured(result.error, params);
1368
+ }
1369
+ }
1370
+ // Re-throw if it's not a ValidationError
1371
+ throw error;
1372
+ }
1373
+ // Additional business logic checks that Zod cannot handle
1374
+ const typedParams = params;
1375
+ const fromData = extractFromData(typedParams);
1376
+ const toData = extractToData(typedParams);
1377
+ // Check for network mismatch if both chains have isTestnet property
1378
+ if (fromData && toData) {
1379
+ validateNetworkMatch(fromData, toData);
1380
+ }
1381
+ // Address format validation with chain context (if address provided)
1382
+ if (toData) {
1383
+ validateAddressFormat(toData);
1384
+ }
1385
+ }
1386
+
1387
+ // -----------------------------------------------------------------------------
1388
+ // Blockchain Enum
1389
+ // -----------------------------------------------------------------------------
1390
+ /**
1391
+ * Enumeration of all blockchains supported by this library.
1392
+ * @enum
1393
+ * @category Enums
1394
+ * @description Provides string identifiers for each supported blockchain.
1395
+ */
1396
+ var Blockchain;
1397
+ (function (Blockchain) {
1398
+ Blockchain["Algorand"] = "Algorand";
1399
+ Blockchain["Algorand_Testnet"] = "Algorand_Testnet";
1400
+ Blockchain["Aptos"] = "Aptos";
1401
+ Blockchain["Aptos_Testnet"] = "Aptos_Testnet";
1402
+ Blockchain["Arbitrum"] = "Arbitrum";
1403
+ Blockchain["Arbitrum_Sepolia"] = "Arbitrum_Sepolia";
1404
+ Blockchain["Avalanche"] = "Avalanche";
1405
+ Blockchain["Avalanche_Fuji"] = "Avalanche_Fuji";
1406
+ Blockchain["Base"] = "Base";
1407
+ Blockchain["Base_Sepolia"] = "Base_Sepolia";
1408
+ Blockchain["Celo"] = "Celo";
1409
+ Blockchain["Celo_Alfajores_Testnet"] = "Celo_Alfajores_Testnet";
1410
+ Blockchain["Codex"] = "Codex";
1411
+ Blockchain["Codex_Testnet"] = "Codex_Testnet";
1412
+ Blockchain["Ethereum"] = "Ethereum";
1413
+ Blockchain["Ethereum_Sepolia"] = "Ethereum_Sepolia";
1414
+ Blockchain["Hedera"] = "Hedera";
1415
+ Blockchain["Hedera_Testnet"] = "Hedera_Testnet";
1416
+ Blockchain["HyperEVM"] = "HyperEVM";
1417
+ Blockchain["HyperEVM_Testnet"] = "HyperEVM_Testnet";
1418
+ Blockchain["Ink"] = "Ink";
1419
+ Blockchain["Ink_Testnet"] = "Ink_Testnet";
1420
+ Blockchain["Linea"] = "Linea";
1421
+ Blockchain["Linea_Sepolia"] = "Linea_Sepolia";
1422
+ Blockchain["NEAR"] = "NEAR";
1423
+ Blockchain["NEAR_Testnet"] = "NEAR_Testnet";
1424
+ Blockchain["Noble"] = "Noble";
1425
+ Blockchain["Noble_Testnet"] = "Noble_Testnet";
1426
+ Blockchain["Optimism"] = "Optimism";
1427
+ Blockchain["Optimism_Sepolia"] = "Optimism_Sepolia";
1428
+ Blockchain["Polkadot_Asset_Hub"] = "Polkadot_Asset_Hub";
1429
+ Blockchain["Polkadot_Westmint"] = "Polkadot_Westmint";
1430
+ Blockchain["Plume"] = "Plume";
1431
+ Blockchain["Plume_Testnet"] = "Plume_Testnet";
1432
+ Blockchain["Polygon"] = "Polygon";
1433
+ Blockchain["Polygon_Amoy_Testnet"] = "Polygon_Amoy_Testnet";
1434
+ Blockchain["Sei"] = "Sei";
1435
+ Blockchain["Sei_Testnet"] = "Sei_Testnet";
1436
+ Blockchain["Solana"] = "Solana";
1437
+ Blockchain["Solana_Devnet"] = "Solana_Devnet";
1438
+ Blockchain["Sonic"] = "Sonic";
1439
+ Blockchain["Sonic_Testnet"] = "Sonic_Testnet";
1440
+ Blockchain["Stellar"] = "Stellar";
1441
+ Blockchain["Stellar_Testnet"] = "Stellar_Testnet";
1442
+ Blockchain["Sui"] = "Sui";
1443
+ Blockchain["Sui_Testnet"] = "Sui_Testnet";
1444
+ Blockchain["Unichain"] = "Unichain";
1445
+ Blockchain["Unichain_Sepolia"] = "Unichain_Sepolia";
1446
+ Blockchain["World_Chain"] = "World_Chain";
1447
+ Blockchain["World_Chain_Sepolia"] = "World_Chain_Sepolia";
1448
+ Blockchain["XDC"] = "XDC";
1449
+ Blockchain["XDC_Apothem"] = "XDC_Apothem";
1450
+ Blockchain["ZKSync_Era"] = "ZKSync_Era";
1451
+ Blockchain["ZKSync_Sepolia"] = "ZKSync_Sepolia";
1452
+ })(Blockchain || (Blockchain = {}));
1453
+
1454
+ /**
1455
+ * Helper function to define a chain with proper TypeScript typing.
1456
+ *
1457
+ * This utility function works with TypeScript's `as const` assertion to create
1458
+ * strongly-typed, immutable chain definition objects. It preserves literal types
1459
+ * from the input and ensures the resulting object maintains all type information.
1460
+ *
1461
+ * When used with `as const`, it allows TypeScript to infer the most specific
1462
+ * possible types for all properties, including string literals and numeric literals,
1463
+ * rather than widening them to general types like string or number.
1464
+ * @typeParam T - The specific chain definition type (must extend ChainDefinition)
1465
+ * @param chain - The chain definition object, typically with an `as const` assertion
1466
+ * @returns The same chain definition with preserved literal types
1467
+ * @example
1468
+ * ```typescript
1469
+ * // Define an EVM chain with literal types preserved
1470
+ * const Ethereum = defineChain({
1471
+ * type: 'evm',
1472
+ * chain: Blockchain.Ethereum,
1473
+ * chainId: 1,
1474
+ * name: 'Ethereum',
1475
+ * nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 },
1476
+ * isTestnet: false,
1477
+ * usdcAddress: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
1478
+ * eurcAddress: null,
1479
+ * cctp: {
1480
+ * domain: 0,
1481
+ * contracts: {
1482
+ * TokenMessengerV1: '0xbd3fa81b58ba92a82136038b25adec7066af3155',
1483
+ * MessageTransmitterV1: '0x0a992d191deec32afe36203ad87d7d289a738f81'
1484
+ * }
1485
+ * }
1486
+ * } as const);
1487
+ * ```
1488
+ */
1489
+ function defineChain(chain) {
1490
+ return chain;
1491
+ }
1492
+
1493
+ /**
1494
+ * Algorand Mainnet chain definition
1495
+ * @remarks
1496
+ * This represents the official production network for the Algorand blockchain.
1497
+ */
1498
+ const Algorand = defineChain({
1499
+ type: 'algorand',
1500
+ chain: Blockchain.Algorand,
1501
+ name: 'Algorand',
1502
+ title: 'Algorand Mainnet',
1503
+ nativeCurrency: {
1504
+ name: 'Algo',
1505
+ symbol: 'ALGO',
1506
+ decimals: 6,
1507
+ },
1508
+ isTestnet: false,
1509
+ explorerUrl: 'https://explorer.perawallet.app/tx/{hash}',
1510
+ rpcEndpoints: ['https://mainnet-api.algonode.cloud'],
1511
+ eurcAddress: null,
1512
+ usdcAddress: '31566704',
1513
+ cctp: null,
1514
+ });
1515
+
1516
+ /**
1517
+ * Algorand Testnet chain definition
1518
+ * @remarks
1519
+ * This represents the official testnet for the Algorand blockchain.
1520
+ */
1521
+ const AlgorandTestnet = defineChain({
1522
+ type: 'algorand',
1523
+ chain: Blockchain.Algorand_Testnet,
1524
+ name: 'Algorand Testnet',
1525
+ title: 'Algorand Test Network',
1526
+ nativeCurrency: {
1527
+ name: 'Algo',
1528
+ symbol: 'ALGO',
1529
+ decimals: 6,
1530
+ },
1531
+ isTestnet: true,
1532
+ explorerUrl: 'https://testnet.explorer.perawallet.app/tx/{hash}',
1533
+ rpcEndpoints: ['https://testnet-api.algonode.cloud'],
1534
+ eurcAddress: null,
1535
+ usdcAddress: '10458941',
1536
+ cctp: null,
1537
+ });
1538
+
1539
+ /**
1540
+ * Aptos Mainnet chain definition
1541
+ * @remarks
1542
+ * This represents the official production network for the Aptos blockchain.
1543
+ */
1544
+ const Aptos = defineChain({
1545
+ type: 'aptos',
1546
+ chain: Blockchain.Aptos,
1547
+ name: 'Aptos',
1548
+ title: 'Aptos Mainnet',
1549
+ nativeCurrency: {
1550
+ name: 'Aptos',
1551
+ symbol: 'APT',
1552
+ decimals: 8,
1553
+ },
1554
+ isTestnet: false,
1555
+ explorerUrl: 'https://explorer.aptoslabs.com/txn/{hash}?network=mainnet',
1556
+ rpcEndpoints: ['https://fullnode.mainnet.aptoslabs.com/v1'],
1557
+ eurcAddress: null,
1558
+ usdcAddress: '0xbae207659db88bea0cbead6da0ed00aac12edcdda169e591cd41c94180b46f3b',
1559
+ cctp: {
1560
+ domain: 9,
1561
+ contracts: {
1562
+ v1: {
1563
+ type: 'split',
1564
+ tokenMessenger: '0x9bce6734f7b63e835108e3bd8c36743d4709fe435f44791918801d0989640a9d',
1565
+ messageTransmitter: '0x177e17751820e4b4371873ca8c30279be63bdea63b88ed0f2239c2eea10f1772',
1566
+ confirmations: 1,
1567
+ },
1568
+ },
1569
+ },
1570
+ });
1571
+
1572
+ /**
1573
+ * Aptos Testnet chain definition
1574
+ * @remarks
1575
+ * This represents the official test network for the Aptos blockchain.
1576
+ */
1577
+ const AptosTestnet = defineChain({
1578
+ type: 'aptos',
1579
+ chain: Blockchain.Aptos_Testnet,
1580
+ name: 'Aptos Testnet',
1581
+ title: 'Aptos Test Network',
1582
+ nativeCurrency: {
1583
+ name: 'Aptos',
1584
+ symbol: 'APT',
1585
+ decimals: 8,
1586
+ },
1587
+ isTestnet: true,
1588
+ explorerUrl: 'https://explorer.aptoslabs.com/txn/{hash}?network=testnet',
1589
+ rpcEndpoints: ['https://fullnode.testnet.aptoslabs.com/v1'],
1590
+ eurcAddress: null,
1591
+ usdcAddress: '0x69091fbab5f7d635ee7ac5098cf0c1efbe31d68fec0f2cd565e8d168daf52832',
1592
+ cctp: {
1593
+ domain: 9,
1594
+ contracts: {
1595
+ v1: {
1596
+ type: 'split',
1597
+ tokenMessenger: '0x5f9b937419dda90aa06c1836b7847f65bbbe3f1217567758dc2488be31a477b9',
1598
+ messageTransmitter: '0x081e86cebf457a0c6004f35bd648a2794698f52e0dde09a48619dcd3d4cc23d9',
1599
+ confirmations: 1,
1600
+ },
1601
+ },
1602
+ },
1603
+ });
1604
+
1605
+ /**
1606
+ * The bridge contract address for EVM testnet networks.
1607
+ *
1608
+ * This contract handles USDC transfers on testnet environments across
1609
+ * EVM-compatible chains. Use this address when deploying or testing
1610
+ * cross-chain USDC transfers on test networks.
1611
+ */
1612
+ const BRIDGE_CONTRACT_EVM_TESTNET = '0xC5567a5E3370d4DBfB0540025078e283e36A363d';
1613
+ /**
1614
+ * The bridge contract address for EVM mainnet networks.
1615
+ *
1616
+ * This contract handles USDC transfers on mainnet environments across
1617
+ * EVM-compatible chains. Use this address for production cross-chain
1618
+ * USDC transfers on live networks.
1619
+ */
1620
+ const BRIDGE_CONTRACT_EVM_MAINNET = '0xB3FA262d0fB521cc93bE83d87b322b8A23DAf3F0';
1621
+
1622
+ /**
1623
+ * Arbitrum Mainnet chain definition
1624
+ * @remarks
1625
+ * This represents the official production network for the Arbitrum blockchain.
1626
+ */
1627
+ const Arbitrum = defineChain({
1628
+ type: 'evm',
1629
+ chain: Blockchain.Arbitrum,
1630
+ name: 'Arbitrum',
1631
+ title: 'Arbitrum Mainnet',
1632
+ nativeCurrency: {
1633
+ name: 'Ether',
1634
+ symbol: 'ETH',
1635
+ decimals: 18,
1636
+ },
1637
+ chainId: 42161,
1638
+ isTestnet: false,
1639
+ explorerUrl: 'https://arbiscan.io/tx/{hash}',
1640
+ rpcEndpoints: ['https://arb1.arbitrum.io/rpc'],
1641
+ eurcAddress: null,
1642
+ usdcAddress: '0xaf88d065e77c8cc2239327c5edb3a432268e5831',
1643
+ cctp: {
1644
+ domain: 3,
1645
+ contracts: {
1646
+ v1: {
1647
+ type: 'split',
1648
+ tokenMessenger: '0x19330d10D9Cc8751218eaf51E8885D058642E08A',
1649
+ messageTransmitter: '0xC30362313FBBA5cf9163F0bb16a0e01f01A896ca',
1650
+ confirmations: 65,
1651
+ },
1652
+ v2: {
1653
+ type: 'split',
1654
+ tokenMessenger: '0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d',
1655
+ messageTransmitter: '0x81D40F21F12A8F0E3252Bccb954D722d4c464B64',
1656
+ confirmations: 65,
1657
+ fastConfirmations: 1,
1658
+ },
1659
+ },
1660
+ },
1661
+ kitContracts: {
1662
+ bridge: BRIDGE_CONTRACT_EVM_MAINNET,
1663
+ },
1664
+ });
1665
+
1666
+ /**
1667
+ * Arbitrum Sepolia Testnet chain definition
1668
+ * @remarks
1669
+ * This represents the official test network for the Arbitrum blockchain on Sepolia.
1670
+ */
1671
+ const ArbitrumSepolia = defineChain({
1672
+ type: 'evm',
1673
+ chain: Blockchain.Arbitrum_Sepolia,
1674
+ name: 'Arbitrum Sepolia',
1675
+ title: 'Arbitrum Sepolia Testnet',
1676
+ nativeCurrency: {
1677
+ name: 'Sepolia Ether',
1678
+ symbol: 'ETH',
1679
+ decimals: 18,
1680
+ },
1681
+ chainId: 421614,
1682
+ isTestnet: true,
1683
+ explorerUrl: 'https://sepolia.arbiscan.io/tx/{hash}',
1684
+ rpcEndpoints: ['https://sepolia-rollup.arbitrum.io/rpc'],
1685
+ eurcAddress: null,
1686
+ usdcAddress: '0x75faf114eafb1BDbe2F0316DF893fd58CE46AA4d',
1687
+ cctp: {
1688
+ domain: 3,
1689
+ contracts: {
1690
+ v1: {
1691
+ type: 'split',
1692
+ tokenMessenger: '0x9f3B8679c73C2Fef8b59B4f3444d4e156fb70AA5',
1693
+ messageTransmitter: '0xaCF1ceeF35caAc005e15888dDb8A3515C41B4872',
1694
+ confirmations: 65,
1695
+ },
1696
+ v2: {
1697
+ type: 'split',
1698
+ tokenMessenger: '0x8FE6B999Dc680CcFDD5Bf7EB0974218be2542DAA',
1699
+ messageTransmitter: '0xE737e5cEBEEBa77EFE34D4aa090756590b1CE275',
1700
+ confirmations: 65,
1701
+ fastConfirmations: 1,
1702
+ },
1703
+ },
1704
+ },
1705
+ kitContracts: {
1706
+ bridge: BRIDGE_CONTRACT_EVM_TESTNET,
1707
+ },
1708
+ });
1709
+
1710
+ /**
1711
+ * Avalanche Mainnet chain definition
1712
+ * @remarks
1713
+ * This represents the official production network for the Avalanche blockchain.
1714
+ */
1715
+ const Avalanche = defineChain({
1716
+ type: 'evm',
1717
+ chain: Blockchain.Avalanche,
1718
+ name: 'Avalanche',
1719
+ title: 'Avalanche Mainnet',
1720
+ nativeCurrency: {
1721
+ name: 'Avalanche',
1722
+ symbol: 'AVAX',
1723
+ decimals: 18,
1724
+ },
1725
+ chainId: 43114,
1726
+ isTestnet: false,
1727
+ explorerUrl: 'https://subnets.avax.network/c-chain/tx/{hash}',
1728
+ rpcEndpoints: ['https://api.avax.network/ext/bc/C/rpc'],
1729
+ eurcAddress: '0xc891eb4cbdeff6e073e859e987815ed1505c2acd',
1730
+ usdcAddress: '0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E',
1731
+ cctp: {
1732
+ domain: 1,
1733
+ contracts: {
1734
+ v1: {
1735
+ type: 'split',
1736
+ tokenMessenger: '0x6b25532e1060ce10cc3b0a99e5683b91bfde6982',
1737
+ messageTransmitter: '0x8186359af5f57fbb40c6b14a588d2a59c0c29880',
1738
+ confirmations: 1,
1739
+ },
1740
+ v2: {
1741
+ type: 'split',
1742
+ tokenMessenger: '0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d',
1743
+ messageTransmitter: '0x81D40F21F12A8F0E3252Bccb954D722d4c464B64',
1744
+ confirmations: 1,
1745
+ fastConfirmations: 1,
1746
+ },
1747
+ },
1748
+ },
1749
+ kitContracts: {
1750
+ bridge: BRIDGE_CONTRACT_EVM_MAINNET,
1751
+ },
1752
+ });
1753
+
1754
+ /**
1755
+ * Avalanche Fuji Testnet chain definition
1756
+ * @remarks
1757
+ * This represents the official test network for the Avalanche blockchain.
1758
+ */
1759
+ const AvalancheFuji = defineChain({
1760
+ type: 'evm',
1761
+ chain: Blockchain.Avalanche_Fuji,
1762
+ name: 'Avalanche Fuji',
1763
+ title: 'Avalanche Fuji Testnet',
1764
+ nativeCurrency: {
1765
+ name: 'Avalanche',
1766
+ symbol: 'AVAX',
1767
+ decimals: 18,
1768
+ },
1769
+ chainId: 43113,
1770
+ isTestnet: true,
1771
+ explorerUrl: 'https://subnets-test.avax.network/c-chain/tx/{hash}',
1772
+ eurcAddress: '0x5e44db7996c682e92a960b65ac713a54ad815c6b',
1773
+ usdcAddress: '0x5425890298aed601595a70ab815c96711a31bc65',
1774
+ cctp: {
1775
+ domain: 1,
1776
+ contracts: {
1777
+ v1: {
1778
+ type: 'split',
1779
+ tokenMessenger: '0xeb08f243e5d3fcff26a9e38ae5520a669f4019d0',
1780
+ messageTransmitter: '0xa9fb1b3009dcb79e2fe346c16a604b8fa8ae0a79',
1781
+ confirmations: 1,
1782
+ },
1783
+ v2: {
1784
+ type: 'split',
1785
+ tokenMessenger: '0x8fe6b999dc680ccfdd5bf7eb0974218be2542daa',
1786
+ messageTransmitter: '0xe737e5cebeeba77efe34d4aa090756590b1ce275',
1787
+ confirmations: 1,
1788
+ fastConfirmations: 1,
1789
+ },
1790
+ },
1791
+ },
1792
+ rpcEndpoints: ['https://api.avax-test.network/ext/bc/C/rpc'],
1793
+ kitContracts: {
1794
+ bridge: BRIDGE_CONTRACT_EVM_TESTNET,
1795
+ },
1796
+ });
1797
+
1798
+ /**
1799
+ * Base chain definition
1800
+ * @remarks
1801
+ * This represents the official production network for the Base blockchain.
1802
+ */
1803
+ const Base = defineChain({
1804
+ type: 'evm',
1805
+ chain: Blockchain.Base,
1806
+ name: 'Base',
1807
+ title: 'Base Mainnet',
1808
+ nativeCurrency: {
1809
+ name: 'Ether',
1810
+ symbol: 'ETH',
1811
+ decimals: 18,
1812
+ },
1813
+ chainId: 8453,
1814
+ isTestnet: false,
1815
+ explorerUrl: 'https://basescan.org/tx/{hash}',
1816
+ rpcEndpoints: ['https://mainnet.base.org', 'https://base.publicnode.com'],
1817
+ eurcAddress: '0x60a3e35cc302bfa44cb288bc5a4f316fdb1adb42',
1818
+ usdcAddress: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',
1819
+ cctp: {
1820
+ domain: 6,
1821
+ contracts: {
1822
+ v1: {
1823
+ type: 'split',
1824
+ tokenMessenger: '0x1682Ae6375C4E4A97e4B583BC394c861A46D8962',
1825
+ messageTransmitter: '0xAD09780d193884d503182aD4588450C416D6F9D4',
1826
+ confirmations: 65,
1827
+ },
1828
+ v2: {
1829
+ type: 'split',
1830
+ tokenMessenger: '0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d',
1831
+ messageTransmitter: '0x81D40F21F12A8F0E3252Bccb954D722d4c464B64',
1832
+ confirmations: 65,
1833
+ fastConfirmations: 1,
1834
+ },
1835
+ },
1836
+ },
1837
+ kitContracts: {
1838
+ bridge: BRIDGE_CONTRACT_EVM_MAINNET,
1839
+ },
1840
+ });
1841
+
1842
+ /**
1843
+ * Base Sepolia Testnet chain definition
1844
+ * @remarks
1845
+ * This represents the official test network for the Base blockchain on Sepolia.
1846
+ */
1847
+ const BaseSepolia = defineChain({
1848
+ type: 'evm',
1849
+ chain: Blockchain.Base_Sepolia,
1850
+ name: 'Base Sepolia',
1851
+ title: 'Base Sepolia Testnet',
1852
+ nativeCurrency: {
1853
+ name: 'Sepolia Ether',
1854
+ symbol: 'ETH',
1855
+ decimals: 18,
1856
+ },
1857
+ chainId: 84532,
1858
+ isTestnet: true,
1859
+ explorerUrl: 'https://sepolia.basescan.org/tx/{hash}',
1860
+ rpcEndpoints: ['https://sepolia.base.org'],
1861
+ eurcAddress: '0x808456652fdb597867f38412077A9182bf77359F',
1862
+ usdcAddress: '0x036CbD53842c5426634e7929541eC2318f3dCF7e',
1863
+ cctp: {
1864
+ domain: 6,
1865
+ contracts: {
1866
+ v1: {
1867
+ type: 'split',
1868
+ tokenMessenger: '0x9f3B8679c73C2Fef8b59B4f3444d4e156fb70AA5',
1869
+ messageTransmitter: '0x7865fAfC2db2093669d92c0F33AeEF291086BEFD',
1870
+ confirmations: 65,
1871
+ },
1872
+ v2: {
1873
+ type: 'split',
1874
+ tokenMessenger: '0x8fe6b999dc680ccfdd5bf7eb0974218be2542daa',
1875
+ messageTransmitter: '0xe737e5cebeeba77efe34d4aa090756590b1ce275',
1876
+ confirmations: 65,
1877
+ fastConfirmations: 1,
1878
+ },
1879
+ },
1880
+ },
1881
+ kitContracts: {
1882
+ bridge: BRIDGE_CONTRACT_EVM_TESTNET,
1883
+ },
1884
+ });
1885
+
1886
+ /**
1887
+ * Celo Mainnet chain definition
1888
+ * @remarks
1889
+ * This represents the official production network for the Celo blockchain.
1890
+ */
1891
+ const Celo = defineChain({
1892
+ type: 'evm',
1893
+ chain: Blockchain.Celo,
1894
+ name: 'Celo',
1895
+ title: 'Celo Mainnet',
1896
+ nativeCurrency: {
1897
+ name: 'Celo',
1898
+ symbol: 'CELO',
1899
+ decimals: 18,
1900
+ },
1901
+ chainId: 42220,
1902
+ isTestnet: false,
1903
+ explorerUrl: 'https://celoscan.io/tx/{hash}',
1904
+ rpcEndpoints: ['https://forno.celo.org'],
1905
+ eurcAddress: null,
1906
+ usdcAddress: '0xcebA9300f2b948710d2653dD7B07f33A8B32118C',
1907
+ cctp: null,
1908
+ });
1909
+
1910
+ /**
1911
+ * Celo Alfajores Testnet chain definition
1912
+ * @remarks
1913
+ * This represents the official test network for the Celo blockchain.
1914
+ */
1915
+ const CeloAlfajoresTestnet = defineChain({
1916
+ type: 'evm',
1917
+ chain: Blockchain.Celo_Alfajores_Testnet,
1918
+ name: 'Celo Alfajores',
1919
+ title: 'Celo Alfajores Testnet',
1920
+ nativeCurrency: {
1921
+ name: 'Celo',
1922
+ symbol: 'CELO',
1923
+ decimals: 18,
1924
+ },
1925
+ chainId: 44787,
1926
+ isTestnet: true,
1927
+ explorerUrl: 'https://alfajores.celoscan.io/tx/{hash}',
1928
+ rpcEndpoints: ['https://alfajores-forno.celo-testnet.org'],
1929
+ eurcAddress: null,
1930
+ usdcAddress: '0x2F25deB3848C207fc8E0c34035B3Ba7fC157602B',
1931
+ cctp: null,
1932
+ });
1933
+
1934
+ /**
1935
+ * Codex Mainnet chain definition
1936
+ * @remarks
1937
+ * This represents the main network for the Codex blockchain.
1938
+ */
1939
+ const Codex = defineChain({
1940
+ type: 'evm',
1941
+ chain: Blockchain.Codex,
1942
+ name: 'Codex Mainnet',
1943
+ title: 'Codex Mainnet',
1944
+ nativeCurrency: {
1945
+ name: 'ETH',
1946
+ symbol: 'ETH',
1947
+ decimals: 18,
1948
+ },
1949
+ chainId: 81224,
1950
+ isTestnet: false,
1951
+ explorerUrl: 'https://explorer.codex.xyz/tx/{hash}',
1952
+ rpcEndpoints: ['https://rpc.codex.xyz'],
1953
+ eurcAddress: null,
1954
+ usdcAddress: '0xd996633a415985DBd7D6D12f4A4343E31f5037cf',
1955
+ cctp: {
1956
+ domain: 12,
1957
+ contracts: {
1958
+ v2: {
1959
+ type: 'split',
1960
+ tokenMessenger: '0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d',
1961
+ messageTransmitter: '0x81D40F21F12A8F0E3252Bccb954D722d4c464B64',
1962
+ confirmations: 65,
1963
+ fastConfirmations: 1,
1964
+ },
1965
+ },
1966
+ },
1967
+ kitContracts: {
1968
+ bridge: BRIDGE_CONTRACT_EVM_MAINNET,
1969
+ },
1970
+ });
1971
+
1972
+ /**
1973
+ * Codex Testnet chain definition
1974
+ * @remarks
1975
+ * This represents the test network for the Codex blockchain.
1976
+ */
1977
+ const CodexTestnet = defineChain({
1978
+ type: 'evm',
1979
+ chain: Blockchain.Codex_Testnet,
1980
+ name: 'Codex Testnet',
1981
+ title: 'Codex Testnet',
1982
+ nativeCurrency: {
1983
+ name: 'ETH',
1984
+ symbol: 'ETH',
1985
+ decimals: 18,
1986
+ },
1987
+ chainId: 812242,
1988
+ isTestnet: true,
1989
+ explorerUrl: 'https://explorer.codex-stg.xyz/tx/{hash}',
1990
+ rpcEndpoints: ['https://rpc.codex-stg.xyz'],
1991
+ eurcAddress: null,
1992
+ usdcAddress: '0x6d7f141b6819C2c9CC2f818e6ad549E7Ca090F8f',
1993
+ cctp: {
1994
+ domain: 12,
1995
+ contracts: {
1996
+ v2: {
1997
+ type: 'split',
1998
+ tokenMessenger: '0x8fe6b999dc680ccfdd5bf7eb0974218be2542daa',
1999
+ messageTransmitter: '0xe737e5cebeeba77efe34d4aa090756590b1ce275',
2000
+ confirmations: 65,
2001
+ fastConfirmations: 1,
2002
+ },
2003
+ },
2004
+ },
2005
+ kitContracts: {
2006
+ bridge: BRIDGE_CONTRACT_EVM_TESTNET,
2007
+ },
2008
+ });
2009
+
2010
+ /**
2011
+ * Ethereum Mainnet chain definition
2012
+ * @remarks
2013
+ * This represents the official production network for the Ethereum blockchain.
2014
+ */
2015
+ const Ethereum = defineChain({
2016
+ type: 'evm',
2017
+ chain: Blockchain.Ethereum,
2018
+ name: 'Ethereum',
2019
+ title: 'Ethereum Mainnet',
2020
+ nativeCurrency: {
2021
+ name: 'Ether',
2022
+ symbol: 'ETH',
2023
+ decimals: 18,
2024
+ },
2025
+ chainId: 1,
2026
+ isTestnet: false,
2027
+ explorerUrl: 'https://etherscan.io/tx/{hash}',
2028
+ rpcEndpoints: ['https://eth.merkle.io', 'https://ethereum.publicnode.com'],
2029
+ eurcAddress: '0x1aBaEA1f7C830bD89Acc67eC4af516284b1bC33c',
2030
+ usdcAddress: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48',
2031
+ cctp: {
2032
+ domain: 0,
2033
+ contracts: {
2034
+ v1: {
2035
+ type: 'split',
2036
+ tokenMessenger: '0xbd3fa81b58ba92a82136038b25adec7066af3155',
2037
+ messageTransmitter: '0x0a992d191deec32afe36203ad87d7d289a738f81',
2038
+ confirmations: 65,
2039
+ },
2040
+ v2: {
2041
+ type: 'split',
2042
+ tokenMessenger: '0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d',
2043
+ messageTransmitter: '0x81D40F21F12A8F0E3252Bccb954D722d4c464B64',
2044
+ confirmations: 65,
2045
+ fastConfirmations: 2,
2046
+ },
2047
+ },
2048
+ },
2049
+ kitContracts: {
2050
+ bridge: BRIDGE_CONTRACT_EVM_MAINNET,
2051
+ },
2052
+ });
2053
+
2054
+ /**
2055
+ * Ethereum Sepolia Testnet chain definition
2056
+ * @remarks
2057
+ * This represents the official test network for the Ethereum blockchain on Sepolia.
2058
+ */
2059
+ const EthereumSepolia = defineChain({
2060
+ type: 'evm',
2061
+ chain: Blockchain.Ethereum_Sepolia,
2062
+ name: 'Ethereum Sepolia',
2063
+ title: 'Ethereum Sepolia Testnet',
2064
+ nativeCurrency: {
2065
+ name: 'Sepolia Ether',
2066
+ symbol: 'ETH',
2067
+ decimals: 18,
2068
+ },
2069
+ chainId: 11155111,
2070
+ isTestnet: true,
2071
+ explorerUrl: 'https://sepolia.etherscan.io/tx/{hash}',
2072
+ rpcEndpoints: ['https://sepolia.drpc.org'],
2073
+ eurcAddress: '0x08210F9170F89Ab7658F0B5E3fF39b0E03C594D4',
2074
+ usdcAddress: '0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238',
2075
+ cctp: {
2076
+ domain: 0,
2077
+ contracts: {
2078
+ v1: {
2079
+ type: 'split',
2080
+ tokenMessenger: '0x9f3B8679c73C2Fef8b59B4f3444d4e156fb70AA5',
2081
+ messageTransmitter: '0x7865fAfC2db2093669d92c0F33AeEF291086BEFD',
2082
+ confirmations: 65,
2083
+ },
2084
+ v2: {
2085
+ type: 'split',
2086
+ tokenMessenger: '0x8fe6b999dc680ccfdd5bf7eb0974218be2542daa',
2087
+ messageTransmitter: '0xe737e5cebeeba77efe34d4aa090756590b1ce275',
2088
+ confirmations: 65,
2089
+ fastConfirmations: 2,
2090
+ },
2091
+ },
2092
+ },
2093
+ kitContracts: {
2094
+ bridge: BRIDGE_CONTRACT_EVM_TESTNET,
2095
+ },
2096
+ });
2097
+
2098
+ /**
2099
+ * Hedera Mainnet chain definition
2100
+ * @remarks
2101
+ * This represents the official production network for the Hedera blockchain.
2102
+ */
2103
+ const Hedera = defineChain({
2104
+ type: 'hedera',
2105
+ chain: Blockchain.Hedera,
2106
+ name: 'Hedera',
2107
+ title: 'Hedera Mainnet',
2108
+ nativeCurrency: {
2109
+ name: 'HBAR',
2110
+ symbol: 'HBAR',
2111
+ decimals: 18,
2112
+ },
2113
+ isTestnet: false,
2114
+ explorerUrl: 'https://hashscan.io/mainnet/transaction/{hash}', // Note: Hedera uses `transaction_id`, not hash. Format is typically `0.0.X-YYYY...`.
2115
+ rpcEndpoints: ['https://mainnet.hashio.io/api'],
2116
+ eurcAddress: null,
2117
+ usdcAddress: '0.0.456858',
2118
+ cctp: null,
2119
+ });
2120
+
2121
+ /**
2122
+ * Hedera Testnet chain definition
2123
+ * @remarks
2124
+ * This represents the official test network for the Hedera blockchain.
2125
+ */
2126
+ const HederaTestnet = defineChain({
2127
+ type: 'hedera',
2128
+ chain: Blockchain.Hedera_Testnet,
2129
+ name: 'Hedera Testnet',
2130
+ title: 'Hedera Test Network',
2131
+ nativeCurrency: {
2132
+ name: 'HBAR',
2133
+ symbol: 'HBAR',
2134
+ decimals: 18,
2135
+ },
2136
+ isTestnet: true,
2137
+ explorerUrl: 'https://hashscan.io/testnet/transaction/{hash}', // Note: Hedera uses `transaction_id`, not hash. Format is typically `0.0.X-YYYY...`.
2138
+ rpcEndpoints: ['https://testnet.hashio.io/api'],
2139
+ eurcAddress: null,
2140
+ usdcAddress: '0.0.429274',
2141
+ cctp: null,
2142
+ });
2143
+
2144
+ /**
2145
+ * HyperEVM Mainnet chain definition
2146
+ * @remarks
2147
+ * This represents the official production network for the HyperEVM blockchain.
2148
+ * HyperEVM is a Layer 1 blockchain specialized for DeFi and trading applications
2149
+ * with native orderbook and matching engine.
2150
+ */
2151
+ const HyperEVM = defineChain({
2152
+ type: 'evm',
2153
+ chain: Blockchain.HyperEVM,
2154
+ name: 'HyperEVM',
2155
+ title: 'HyperEVM Mainnet',
2156
+ nativeCurrency: {
2157
+ name: 'Hype',
2158
+ symbol: 'HYPE',
2159
+ decimals: 18,
2160
+ },
2161
+ chainId: 999,
2162
+ isTestnet: false,
2163
+ explorerUrl: 'https://hyperevmscan.io/tx/{hash}',
2164
+ rpcEndpoints: ['https://rpc.hyperliquid.xyz/evm'],
2165
+ eurcAddress: null,
2166
+ usdcAddress: '0xb88339CB7199b77E23DB6E890353E22632Ba630f',
2167
+ cctp: {
2168
+ domain: 19,
2169
+ contracts: {
2170
+ v2: {
2171
+ type: 'split',
2172
+ tokenMessenger: '0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d',
2173
+ messageTransmitter: '0x81D40F21F12A8F0E3252Bccb954D722d4c464B64',
2174
+ confirmations: 1,
2175
+ fastConfirmations: 1,
2176
+ },
2177
+ },
2178
+ },
2179
+ kitContracts: {
2180
+ bridge: BRIDGE_CONTRACT_EVM_MAINNET,
2181
+ },
2182
+ });
2183
+
2184
+ /**
2185
+ * HyperEVM Testnet chain definition
2186
+ * @remarks
2187
+ * This represents the official testnet for the HyperEVM blockchain.
2188
+ * Used for development and testing purposes before deploying to mainnet.
2189
+ */
2190
+ const HyperEVMTestnet = defineChain({
2191
+ type: 'evm',
2192
+ chain: Blockchain.HyperEVM_Testnet,
2193
+ name: 'HyperEVM Testnet',
2194
+ title: 'HyperEVM Test Network',
2195
+ nativeCurrency: {
2196
+ name: 'Hype',
2197
+ symbol: 'HYPE',
2198
+ decimals: 18,
2199
+ },
2200
+ chainId: 998,
2201
+ isTestnet: true,
2202
+ explorerUrl: 'https://testnet.hyperliquid.xyz/explorer/tx/{hash}',
2203
+ rpcEndpoints: ['https://rpc.hyperliquid-testnet.xyz/evm'],
2204
+ eurcAddress: null,
2205
+ usdcAddress: '0x2B3370eE501B4a559b57D449569354196457D8Ab',
2206
+ cctp: {
2207
+ domain: 19,
2208
+ contracts: {
2209
+ v2: {
2210
+ type: 'split',
2211
+ tokenMessenger: '0x8FE6B999Dc680CcFDD5Bf7EB0974218be2542DAA',
2212
+ messageTransmitter: '0xE737e5cEBEEBa77EFE34D4aa090756590b1CE275',
2213
+ confirmations: 1,
2214
+ fastConfirmations: 1,
2215
+ },
2216
+ },
2217
+ },
2218
+ kitContracts: {
2219
+ bridge: BRIDGE_CONTRACT_EVM_TESTNET,
2220
+ },
2221
+ });
2222
+
2223
+ /**
2224
+ * Ink Mainnet chain definition
2225
+ * @remarks
2226
+ * This represents the official production network for the Ink blockchain.
2227
+ * Ink is a Layer 1 blockchain specialized for DeFi and trading applications
2228
+ * with native orderbook and matching engine.
2229
+ */
2230
+ const Ink = defineChain({
2231
+ type: 'evm',
2232
+ chain: Blockchain.Ink,
2233
+ name: 'Ink',
2234
+ title: 'Ink Mainnet',
2235
+ nativeCurrency: {
2236
+ name: 'Ether',
2237
+ symbol: 'ETH',
2238
+ decimals: 18,
2239
+ },
2240
+ chainId: 57073,
2241
+ isTestnet: false,
2242
+ explorerUrl: 'https://explorer.inkonchain.com/tx/{hash}',
2243
+ rpcEndpoints: [
2244
+ 'https://rpc-gel.inkonchain.com',
2245
+ 'https://rpc-qnd.inkonchain.com',
2246
+ ],
2247
+ eurcAddress: null,
2248
+ usdcAddress: '0x2D270e6886d130D724215A266106e6832161EAEd',
2249
+ cctp: {
2250
+ domain: 21,
2251
+ contracts: {
2252
+ v2: {
2253
+ type: 'split',
2254
+ tokenMessenger: '0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d',
2255
+ messageTransmitter: '0x81D40F21F12A8F0E3252Bccb954D722d4c464B64',
2256
+ confirmations: 65,
2257
+ fastConfirmations: 1,
2258
+ },
2259
+ },
2260
+ },
2261
+ kitContracts: {
2262
+ bridge: BRIDGE_CONTRACT_EVM_MAINNET,
2263
+ },
2264
+ });
2265
+
2266
+ /**
2267
+ * Ink Testnet chain definition
2268
+ * @remarks
2269
+ * This represents the official testnet for the Ink blockchain.
2270
+ * Used for development and testing purposes before deploying to mainnet.
2271
+ */
2272
+ const InkTestnet = defineChain({
2273
+ type: 'evm',
2274
+ chain: Blockchain.Ink_Testnet,
2275
+ name: 'Ink Sepolia',
2276
+ title: 'Ink Sepolia Testnet',
2277
+ nativeCurrency: {
2278
+ name: 'Sepolia Ether',
2279
+ symbol: 'ETH',
2280
+ decimals: 18,
2281
+ },
2282
+ chainId: 763373,
2283
+ isTestnet: true,
2284
+ explorerUrl: 'https://explorer-sepolia.inkonchain.com/tx/{hash}',
2285
+ rpcEndpoints: [
2286
+ 'https://rpc-gel-sepolia.inkonchain.com',
2287
+ 'https://rpc-qnd-sepolia.inkonchain.com',
2288
+ ],
2289
+ eurcAddress: null,
2290
+ usdcAddress: '0xFabab97dCE620294D2B0b0e46C68964e326300Ac',
2291
+ cctp: {
2292
+ domain: 21,
2293
+ contracts: {
2294
+ v2: {
2295
+ type: 'split',
2296
+ tokenMessenger: '0x8FE6B999Dc680CcFDD5Bf7EB0974218be2542DAA',
2297
+ messageTransmitter: '0xE737e5cEBEEBa77EFE34D4aa090756590b1CE275',
2298
+ confirmations: 65,
2299
+ fastConfirmations: 1,
2300
+ },
2301
+ },
2302
+ },
2303
+ kitContracts: {
2304
+ bridge: BRIDGE_CONTRACT_EVM_TESTNET,
2305
+ },
2306
+ });
2307
+
2308
+ /**
2309
+ * Linea Mainnet chain definition
2310
+ * @remarks
2311
+ * This represents the official production network for the Linea blockchain.
2312
+ */
2313
+ const Linea = defineChain({
2314
+ type: 'evm',
2315
+ chain: Blockchain.Linea,
2316
+ name: 'Linea',
2317
+ title: 'Linea Mainnet',
2318
+ nativeCurrency: {
2319
+ name: 'Ether',
2320
+ symbol: 'ETH',
2321
+ decimals: 18,
2322
+ },
2323
+ chainId: 59144,
2324
+ isTestnet: false,
2325
+ explorerUrl: 'https://lineascan.build/tx/{hash}',
2326
+ rpcEndpoints: ['https://rpc.linea.build'],
2327
+ eurcAddress: null,
2328
+ usdcAddress: '0x176211869ca2b568f2a7d4ee941e073a821ee1ff',
2329
+ cctp: {
2330
+ domain: 11,
2331
+ contracts: {
2332
+ v2: {
2333
+ type: 'split',
2334
+ tokenMessenger: '0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d',
2335
+ messageTransmitter: '0x81D40F21F12A8F0E3252Bccb954D722d4c464B64',
2336
+ confirmations: 1,
2337
+ fastConfirmations: 1,
2338
+ },
2339
+ },
2340
+ },
2341
+ kitContracts: {
2342
+ bridge: BRIDGE_CONTRACT_EVM_MAINNET,
2343
+ },
2344
+ });
2345
+
2346
+ /**
2347
+ * Linea Sepolia Testnet chain definition
2348
+ * @remarks
2349
+ * This represents the official test network for the Linea blockchain on Sepolia.
2350
+ */
2351
+ const LineaSepolia = defineChain({
2352
+ type: 'evm',
2353
+ chain: Blockchain.Linea_Sepolia,
2354
+ name: 'Linea Sepolia',
2355
+ title: 'Linea Sepolia Testnet',
2356
+ nativeCurrency: {
2357
+ name: 'Sepolia Ether',
2358
+ symbol: 'ETH',
2359
+ decimals: 18,
2360
+ },
2361
+ chainId: 59141,
2362
+ isTestnet: true,
2363
+ explorerUrl: 'https://sepolia.lineascan.build/tx/{hash}',
2364
+ rpcEndpoints: ['https://rpc.sepolia.linea.build'],
2365
+ eurcAddress: null,
2366
+ usdcAddress: '0xfece4462d57bd51a6a552365a011b95f0e16d9b7',
2367
+ cctp: {
2368
+ domain: 11,
2369
+ contracts: {
2370
+ v2: {
2371
+ type: 'split',
2372
+ tokenMessenger: '0x8fe6b999dc680ccfdd5bf7eb0974218be2542daa',
2373
+ messageTransmitter: '0xe737e5cebeeba77efe34d4aa090756590b1ce275',
2374
+ confirmations: 1,
2375
+ fastConfirmations: 1,
2376
+ },
2377
+ },
2378
+ },
2379
+ kitContracts: {
2380
+ bridge: BRIDGE_CONTRACT_EVM_TESTNET,
2381
+ },
2382
+ });
2383
+
2384
+ /**
2385
+ * NEAR Protocol Mainnet chain definition
2386
+ * @remarks
2387
+ * This represents the official production network for the NEAR Protocol blockchain.
2388
+ */
2389
+ const NEAR = defineChain({
2390
+ type: 'near',
2391
+ chain: Blockchain.NEAR,
2392
+ name: 'NEAR Protocol',
2393
+ title: 'NEAR Mainnet',
2394
+ nativeCurrency: {
2395
+ name: 'NEAR',
2396
+ symbol: 'NEAR',
2397
+ decimals: 24,
2398
+ },
2399
+ isTestnet: false,
2400
+ explorerUrl: 'https://nearblocks.io/txns/{hash}',
2401
+ rpcEndpoints: ['https://eth-rpc.mainnet.near.org'],
2402
+ eurcAddress: null,
2403
+ usdcAddress: '17208628f84f5d6ad33f0da3bbbeb27ffcb398eac501a31bd6ad2011e36133a1',
2404
+ cctp: null,
2405
+ });
2406
+
2407
+ /**
2408
+ * NEAR Testnet chain definition
2409
+ * @remarks
2410
+ * This represents the official test network for the NEAR Protocol blockchain.
2411
+ */
2412
+ const NEARTestnet = defineChain({
2413
+ type: 'near',
2414
+ chain: Blockchain.NEAR_Testnet,
2415
+ name: 'NEAR Protocol Testnet',
2416
+ title: 'NEAR Test Network',
2417
+ nativeCurrency: {
2418
+ name: 'NEAR',
2419
+ symbol: 'NEAR',
2420
+ decimals: 24,
2421
+ },
2422
+ isTestnet: true,
2423
+ explorerUrl: 'https://testnet.nearblocks.io/txns/{hash}',
2424
+ rpcEndpoints: ['https://eth-rpc.testnet.near.org'],
2425
+ eurcAddress: null,
2426
+ usdcAddress: '3e2210e1184b45b64c8a434c0a7e7b23cc04ea7eb7a6c3c32520d03d4afcb8af',
2427
+ cctp: null,
2428
+ });
2429
+
2430
+ /**
2431
+ * Noble Mainnet chain definition
2432
+ * @remarks
2433
+ * This represents the official production network for the Noble blockchain.
2434
+ */
2435
+ const Noble = defineChain({
2436
+ type: 'noble',
2437
+ chain: Blockchain.Noble,
2438
+ name: 'Noble',
2439
+ title: 'Noble Mainnet',
2440
+ nativeCurrency: {
2441
+ name: 'Noble USDC',
2442
+ symbol: 'USDC',
2443
+ decimals: 6,
2444
+ },
2445
+ isTestnet: false,
2446
+ explorerUrl: 'https://www.mintscan.io/noble/tx/{hash}',
2447
+ rpcEndpoints: ['https://noble-rpc.polkachu.com'],
2448
+ eurcAddress: null,
2449
+ usdcAddress: 'uusdc',
2450
+ cctp: {
2451
+ domain: 4,
2452
+ contracts: {
2453
+ v1: {
2454
+ type: 'merged',
2455
+ contract: 'noble12l2w4ugfz4m6dd73yysz477jszqnfughxvkss5',
2456
+ confirmations: 1,
2457
+ },
2458
+ },
2459
+ },
2460
+ });
2461
+
2462
+ /**
2463
+ * Noble Testnet chain definition
2464
+ * @remarks
2465
+ * This represents the official test network for the Noble blockchain.
2466
+ */
2467
+ const NobleTestnet = defineChain({
2468
+ type: 'noble',
2469
+ chain: Blockchain.Noble_Testnet,
2470
+ name: 'Noble Testnet',
2471
+ title: 'Noble Test Network',
2472
+ nativeCurrency: {
2473
+ name: 'Noble USDC',
2474
+ symbol: 'USDC',
2475
+ decimals: 6,
2476
+ },
2477
+ isTestnet: true,
2478
+ explorerUrl: 'https://www.mintscan.io/noble-testnet/tx/{hash}',
2479
+ rpcEndpoints: ['https://noble-testnet-rpc.polkachu.com'],
2480
+ eurcAddress: null,
2481
+ usdcAddress: 'uusdc',
2482
+ cctp: {
2483
+ domain: 4,
2484
+ contracts: {
2485
+ v1: {
2486
+ type: 'merged',
2487
+ contract: 'noble12l2w4ugfz4m6dd73yysz477jszqnfughxvkss5',
2488
+ confirmations: 1,
2489
+ },
2490
+ },
2491
+ },
2492
+ });
2493
+
2494
+ /**
2495
+ * Optimism Mainnet chain definition
2496
+ * @remarks
2497
+ * This represents the official production network for the Optimism blockchain.
2498
+ */
2499
+ const Optimism = defineChain({
2500
+ type: 'evm',
2501
+ chain: Blockchain.Optimism,
2502
+ name: 'Optimism',
2503
+ title: 'Optimism Mainnet',
2504
+ nativeCurrency: {
2505
+ name: 'Ether',
2506
+ symbol: 'ETH',
2507
+ decimals: 18,
2508
+ },
2509
+ chainId: 10,
2510
+ isTestnet: false,
2511
+ explorerUrl: 'https://optimistic.etherscan.io/tx/{hash}',
2512
+ rpcEndpoints: ['https://mainnet.optimism.io'],
2513
+ eurcAddress: null,
2514
+ usdcAddress: '0x0b2c639c533813f4aa9d7837caf62653d097ff85',
2515
+ cctp: {
2516
+ domain: 2,
2517
+ contracts: {
2518
+ v1: {
2519
+ type: 'split',
2520
+ tokenMessenger: '0x2B4069517957735bE00ceE0fadAE88a26365528f',
2521
+ messageTransmitter: '0x0a992d191deec32afe36203ad87d7d289a738f81',
2522
+ confirmations: 65,
2523
+ },
2524
+ v2: {
2525
+ type: 'split',
2526
+ tokenMessenger: '0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d',
2527
+ messageTransmitter: '0x81D40F21F12A8F0E3252Bccb954D722d4c464B64',
2528
+ confirmations: 65,
2529
+ fastConfirmations: 1,
2530
+ },
2531
+ },
2532
+ },
2533
+ kitContracts: {
2534
+ bridge: BRIDGE_CONTRACT_EVM_MAINNET,
2535
+ },
2536
+ });
2537
+
2538
+ /**
2539
+ * Optimism Sepolia Testnet chain definition
2540
+ * @remarks
2541
+ * This represents the official test network for the Optimism blockchain on Sepolia.
2542
+ */
2543
+ const OptimismSepolia = defineChain({
2544
+ type: 'evm',
2545
+ chain: Blockchain.Optimism_Sepolia,
2546
+ name: 'Optimism Sepolia',
2547
+ title: 'Optimism Sepolia Testnet',
2548
+ nativeCurrency: {
2549
+ name: 'Sepolia Ether',
2550
+ symbol: 'ETH',
2551
+ decimals: 18,
2552
+ },
2553
+ chainId: 11155420,
2554
+ isTestnet: true,
2555
+ explorerUrl: 'https://sepolia-optimistic.etherscan.io/tx/{hash}',
2556
+ rpcEndpoints: ['https://sepolia.optimism.io'],
2557
+ eurcAddress: null,
2558
+ usdcAddress: '0x5fd84259d66Cd46123540766Be93DFE6D43130D7',
2559
+ cctp: {
2560
+ domain: 2,
2561
+ contracts: {
2562
+ v1: {
2563
+ type: 'split',
2564
+ tokenMessenger: '0x9f3B8679c73C2Fef8b59B4f3444d4e156fb70AA5',
2565
+ messageTransmitter: '0x7865fAfC2db2093669d92c0F33AeEF291086BEFD',
2566
+ confirmations: 65,
2567
+ },
2568
+ v2: {
2569
+ type: 'split',
2570
+ tokenMessenger: '0x8fe6b999dc680ccfdd5bf7eb0974218be2542daa',
2571
+ messageTransmitter: '0xe737e5cebeeba77efe34d4aa090756590b1ce275',
2572
+ confirmations: 65,
2573
+ fastConfirmations: 1,
2574
+ },
2575
+ },
2576
+ },
2577
+ kitContracts: {
2578
+ bridge: BRIDGE_CONTRACT_EVM_TESTNET,
2579
+ },
2580
+ });
2581
+
2582
+ /**
2583
+ * Plume Mainnet chain definition
2584
+ * @remarks
2585
+ * This represents the official production network for the Plume blockchain.
2586
+ * Plume is a Layer 1 blockchain specialized for DeFi and trading applications
2587
+ * with native orderbook and matching engine.
2588
+ */
2589
+ const Plume = defineChain({
2590
+ type: 'evm',
2591
+ chain: Blockchain.Plume,
2592
+ name: 'Plume',
2593
+ title: 'Plume Mainnet',
2594
+ nativeCurrency: {
2595
+ name: 'Plume',
2596
+ symbol: 'PLUME',
2597
+ decimals: 18,
2598
+ },
2599
+ chainId: 98866,
2600
+ isTestnet: false,
2601
+ explorerUrl: 'https://explorer.plume.org/tx/{hash}',
2602
+ rpcEndpoints: ['https://rpc.plume.org'],
2603
+ eurcAddress: null,
2604
+ usdcAddress: '0x222365EF19F7947e5484218551B56bb3965Aa7aF',
2605
+ cctp: {
2606
+ domain: 22,
2607
+ contracts: {
2608
+ v2: {
2609
+ type: 'split',
2610
+ tokenMessenger: '0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d',
2611
+ messageTransmitter: '0x81D40F21F12A8F0E3252Bccb954D722d4c464B64',
2612
+ confirmations: 65,
2613
+ fastConfirmations: 1,
2614
+ },
2615
+ },
2616
+ },
2617
+ kitContracts: {
2618
+ bridge: BRIDGE_CONTRACT_EVM_MAINNET,
2619
+ },
2620
+ });
2621
+
2622
+ /**
2623
+ * Plume Testnet chain definition
2624
+ * @remarks
2625
+ * This represents the official testnet for the Plume blockchain.
2626
+ * Used for development and testing purposes before deploying to mainnet.
2627
+ */
2628
+ const PlumeTestnet = defineChain({
2629
+ type: 'evm',
2630
+ chain: Blockchain.Plume_Testnet,
2631
+ name: 'Plume Testnet',
2632
+ title: 'Plume Test Network',
2633
+ nativeCurrency: {
2634
+ name: 'Plume',
2635
+ symbol: 'PLUME',
2636
+ decimals: 18,
2637
+ },
2638
+ chainId: 98867,
2639
+ isTestnet: true,
2640
+ explorerUrl: 'https://testnet-explorer.plume.org/tx/{hash}',
2641
+ rpcEndpoints: ['https://testnet-rpc.plume.org'],
2642
+ eurcAddress: null,
2643
+ usdcAddress: '0xcB5f30e335672893c7eb944B374c196392C19D18',
2644
+ cctp: {
2645
+ domain: 22,
2646
+ contracts: {
2647
+ v2: {
2648
+ type: 'split',
2649
+ tokenMessenger: '0x8FE6B999Dc680CcFDD5Bf7EB0974218be2542DAA',
2650
+ messageTransmitter: '0xE737e5cEBEEBa77EFE34D4aa090756590b1CE275',
2651
+ confirmations: 65,
2652
+ fastConfirmations: 1,
2653
+ },
2654
+ },
2655
+ },
2656
+ kitContracts: {
2657
+ bridge: BRIDGE_CONTRACT_EVM_TESTNET,
2658
+ },
2659
+ });
2660
+
2661
+ /**
2662
+ * Polkadot Asset Hub chain definition
2663
+ * @remarks
2664
+ * This represents the official asset management parachain for the Polkadot blockchain.
2665
+ */
2666
+ const PolkadotAssetHub = defineChain({
2667
+ type: 'polkadot',
2668
+ chain: Blockchain.Polkadot_Asset_Hub,
2669
+ name: 'Polkadot Asset Hub',
2670
+ title: 'Polkadot Asset Hub',
2671
+ nativeCurrency: {
2672
+ name: 'Polkadot',
2673
+ symbol: 'DOT',
2674
+ decimals: 10,
2675
+ },
2676
+ isTestnet: false,
2677
+ explorerUrl: 'https://polkadot.subscan.io/extrinsic/{hash}',
2678
+ rpcEndpoints: ['https://asset-hub-polkadot-rpc.n.dwellir.com'],
2679
+ eurcAddress: null,
2680
+ usdcAddress: '1337',
2681
+ cctp: null,
2682
+ });
2683
+
2684
+ /**
2685
+ * Polkadot Westmint chain definition
2686
+ * @remarks
2687
+ * This represents an asset management parachain in the Polkadot ecosystem.
2688
+ */
2689
+ const PolkadotWestmint = defineChain({
2690
+ type: 'polkadot',
2691
+ chain: Blockchain.Polkadot_Westmint,
2692
+ name: 'Polkadot Westmint',
2693
+ title: 'Polkadot Westmint',
2694
+ nativeCurrency: {
2695
+ name: 'Polkadot',
2696
+ symbol: 'DOT',
2697
+ decimals: 10,
2698
+ },
2699
+ isTestnet: false,
2700
+ explorerUrl: 'https://assethub-polkadot.subscan.io/extrinsic/{hash}',
2701
+ rpcEndpoints: ['https://westmint-rpc.polkadot.io'],
2702
+ eurcAddress: null,
2703
+ usdcAddress: 'Asset ID 31337',
2704
+ cctp: null,
2705
+ });
2706
+
2707
+ /**
2708
+ * Polygon Mainnet chain definition
2709
+ * @remarks
2710
+ * This represents the official production network for the Polygon blockchain.
2711
+ */
2712
+ const Polygon = defineChain({
2713
+ type: 'evm',
2714
+ chain: Blockchain.Polygon,
2715
+ name: 'Polygon',
2716
+ title: 'Polygon Mainnet',
2717
+ nativeCurrency: {
2718
+ name: 'POL',
2719
+ symbol: 'POL',
2720
+ decimals: 18,
2721
+ },
2722
+ chainId: 137,
2723
+ isTestnet: false,
2724
+ explorerUrl: 'https://polygonscan.com/tx/{hash}',
2725
+ rpcEndpoints: ['https://polygon-rpc.com', 'https://polygon.publicnode.com'],
2726
+ eurcAddress: null,
2727
+ usdcAddress: '0x3c499c542cef5e3811e1192ce70d8cc03d5c3359',
2728
+ cctp: {
2729
+ domain: 7,
2730
+ contracts: {
2731
+ v1: {
2732
+ type: 'split',
2733
+ tokenMessenger: '0x9daF8c91AEFAE50b9c0E69629D3F6Ca40cA3B3FE',
2734
+ messageTransmitter: '0xF3be9355363857F3e001be68856A2f96b4C39Ba9',
2735
+ confirmations: 200,
2736
+ },
2737
+ v2: {
2738
+ type: 'split',
2739
+ tokenMessenger: '0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d',
2740
+ messageTransmitter: '0x81D40F21F12A8F0E3252Bccb954D722d4c464B64',
2741
+ confirmations: 33,
2742
+ fastConfirmations: 13,
2743
+ },
2744
+ },
2745
+ },
2746
+ kitContracts: {
2747
+ bridge: BRIDGE_CONTRACT_EVM_MAINNET,
2748
+ },
2749
+ });
2750
+
2751
+ /**
2752
+ * Polygon Amoy Testnet chain definition
2753
+ * @remarks
2754
+ * This represents the official test network for the Polygon blockchain.
2755
+ */
2756
+ const PolygonAmoy = defineChain({
2757
+ type: 'evm',
2758
+ chain: Blockchain.Polygon_Amoy_Testnet,
2759
+ name: 'Polygon Amoy',
2760
+ title: 'Polygon Amoy Testnet',
2761
+ nativeCurrency: {
2762
+ name: 'POL',
2763
+ symbol: 'POL',
2764
+ decimals: 18,
2765
+ },
2766
+ chainId: 80002,
2767
+ isTestnet: true,
2768
+ explorerUrl: 'https://amoy.polygonscan.com/tx/{hash}',
2769
+ rpcEndpoints: ['https://rpc-amoy.polygon.technology'],
2770
+ eurcAddress: null,
2771
+ usdcAddress: '0x41e94eb019c0762f9bfcf9fb1e58725bfb0e7582',
2772
+ cctp: {
2773
+ domain: 7,
2774
+ contracts: {
2775
+ v1: {
2776
+ type: 'split',
2777
+ tokenMessenger: '0x9f3B8679c73C2Fef8b59B4f3444d4e156fb70AA5',
2778
+ messageTransmitter: '0x7865fAfC2db2093669d92c0F33AeEF291086BEFD',
2779
+ confirmations: 200,
2780
+ },
2781
+ v2: {
2782
+ type: 'split',
2783
+ tokenMessenger: '0x8FE6B999Dc680CcFDD5Bf7EB0974218be2542DAA',
2784
+ messageTransmitter: '0xE737e5cEBEEBa77EFE34D4aa090756590b1CE275',
2785
+ confirmations: 33,
2786
+ fastConfirmations: 13,
2787
+ },
2788
+ },
2789
+ },
2790
+ kitContracts: {
2791
+ bridge: BRIDGE_CONTRACT_EVM_TESTNET,
2792
+ },
2793
+ });
2794
+
2795
+ /**
2796
+ * Sei Mainnet chain definition
2797
+ * @remarks
2798
+ * This represents the official production network for the Sei blockchain.
2799
+ * Sei is a Layer 1 blockchain specialized for DeFi and trading applications
2800
+ * with native orderbook and matching engine.
2801
+ */
2802
+ const Sei = defineChain({
2803
+ type: 'evm',
2804
+ chain: Blockchain.Sei,
2805
+ name: 'Sei',
2806
+ title: 'Sei Mainnet',
2807
+ nativeCurrency: {
2808
+ name: 'Sei',
2809
+ symbol: 'SEI',
2810
+ decimals: 18,
2811
+ },
2812
+ chainId: 1329,
2813
+ isTestnet: false,
2814
+ explorerUrl: 'https://seitrace.com/tx/{hash}?chain=pacific-1',
2815
+ rpcEndpoints: ['https://evm-rpc.sei-apis.com'],
2816
+ eurcAddress: null,
2817
+ usdcAddress: '0xe15fC38F6D8c56aF07bbCBe3BAf5708A2Bf42392',
2818
+ cctp: {
2819
+ domain: 16,
2820
+ contracts: {
2821
+ v2: {
2822
+ type: 'split',
2823
+ tokenMessenger: '0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d',
2824
+ messageTransmitter: '0x81D40F21F12A8F0E3252Bccb954D722d4c464B64',
2825
+ confirmations: 1,
2826
+ fastConfirmations: 1,
2827
+ },
2828
+ },
2829
+ },
2830
+ kitContracts: {
2831
+ bridge: BRIDGE_CONTRACT_EVM_MAINNET,
2832
+ },
2833
+ });
2834
+
2835
+ /**
2836
+ * Sei Testnet chain definition
2837
+ * @remarks
2838
+ * This represents the official testnet for the Sei blockchain.
2839
+ * Used for development and testing purposes before deploying to mainnet.
2840
+ */
2841
+ const SeiTestnet = defineChain({
2842
+ type: 'evm',
2843
+ chain: Blockchain.Sei_Testnet,
2844
+ name: 'Sei Testnet',
2845
+ title: 'Sei Test Network',
2846
+ nativeCurrency: {
2847
+ name: 'Sei',
2848
+ symbol: 'SEI',
2849
+ decimals: 18,
2850
+ },
2851
+ chainId: 1328,
2852
+ isTestnet: true,
2853
+ explorerUrl: 'https://seitrace.com/tx/{hash}?chain=atlantic-2',
2854
+ rpcEndpoints: ['https://evm-rpc-testnet.sei-apis.com'],
2855
+ eurcAddress: null,
2856
+ usdcAddress: '0x4fCF1784B31630811181f670Aea7A7bEF803eaED',
2857
+ cctp: {
2858
+ domain: 16,
2859
+ contracts: {
2860
+ v2: {
2861
+ type: 'split',
2862
+ tokenMessenger: '0x8FE6B999Dc680CcFDD5Bf7EB0974218be2542DAA',
2863
+ messageTransmitter: '0xE737e5cEBEEBa77EFE34D4aa090756590b1CE275',
2864
+ confirmations: 1,
2865
+ fastConfirmations: 1,
2866
+ },
2867
+ },
2868
+ },
2869
+ kitContracts: {
2870
+ bridge: BRIDGE_CONTRACT_EVM_TESTNET,
2871
+ },
2872
+ });
2873
+
2874
+ /**
2875
+ * Sonic Mainnet chain definition
2876
+ * @remarks
2877
+ * This represents the official production network for the Sonic blockchain.
2878
+ */
2879
+ const Sonic = defineChain({
2880
+ type: 'evm',
2881
+ chain: Blockchain.Sonic,
2882
+ name: 'Sonic',
2883
+ title: 'Sonic Mainnet',
2884
+ nativeCurrency: {
2885
+ name: 'Sonic',
2886
+ symbol: 'S',
2887
+ decimals: 18,
2888
+ },
2889
+ chainId: 146,
2890
+ isTestnet: false,
2891
+ explorerUrl: 'https://sonicscan.org/tx/{hash}',
2892
+ rpcEndpoints: ['https://rpc.soniclabs.com'],
2893
+ eurcAddress: null,
2894
+ usdcAddress: '0x29219dd400f2Bf60E5a23d13Be72B486D4038894',
2895
+ cctp: {
2896
+ domain: 13,
2897
+ contracts: {
2898
+ v2: {
2899
+ type: 'split',
2900
+ tokenMessenger: '0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d',
2901
+ messageTransmitter: '0x81D40F21F12A8F0E3252Bccb954D722d4c464B64',
2902
+ confirmations: 1,
2903
+ fastConfirmations: 1,
2904
+ },
2905
+ },
2906
+ },
2907
+ kitContracts: {
2908
+ bridge: BRIDGE_CONTRACT_EVM_MAINNET,
2909
+ },
2910
+ });
2911
+
2912
+ /**
2913
+ * Sonic Blaze Testnet chain definition
2914
+ * @remarks
2915
+ * This represents the official test network for the Sonic blockchain.
2916
+ */
2917
+ const SonicTestnet = defineChain({
2918
+ type: 'evm',
2919
+ chain: Blockchain.Sonic_Testnet,
2920
+ name: 'Sonic Blaze Testnet',
2921
+ title: 'Sonic Blaze Testnet',
2922
+ nativeCurrency: {
2923
+ name: 'Sonic',
2924
+ symbol: 'S',
2925
+ decimals: 18,
2926
+ },
2927
+ chainId: 57054,
2928
+ isTestnet: true,
2929
+ explorerUrl: 'https://testnet.sonicscan.org/tx/{hash}',
2930
+ rpcEndpoints: ['https://rpc.blaze.soniclabs.com'],
2931
+ eurcAddress: null,
2932
+ usdcAddress: '0xA4879Fed32Ecbef99399e5cbC247E533421C4eC6',
2933
+ cctp: {
2934
+ domain: 13,
2935
+ contracts: {
2936
+ v2: {
2937
+ type: 'split',
2938
+ tokenMessenger: '0x8FE6B999Dc680CcFDD5Bf7EB0974218be2542DAA',
2939
+ messageTransmitter: '0xE737e5cEBEEBa77EFE34D4aa090756590b1CE275',
2940
+ confirmations: 1,
2941
+ fastConfirmations: 1,
2942
+ },
2943
+ },
2944
+ },
2945
+ kitContracts: {
2946
+ bridge: BRIDGE_CONTRACT_EVM_TESTNET,
2947
+ },
2948
+ });
2949
+
2950
+ /**
2951
+ * Solana Mainnet chain definition
2952
+ * @remarks
2953
+ * This represents the official production network for the Solana blockchain.
2954
+ */
2955
+ const Solana = defineChain({
2956
+ type: 'solana',
2957
+ chain: Blockchain.Solana,
2958
+ name: 'Solana',
2959
+ title: 'Solana Mainnet',
2960
+ nativeCurrency: {
2961
+ name: 'Solana',
2962
+ symbol: 'SOL',
2963
+ decimals: 9,
2964
+ },
2965
+ isTestnet: false,
2966
+ explorerUrl: 'https://solscan.io/tx/{hash}',
2967
+ rpcEndpoints: ['https://api.mainnet-beta.solana.com'],
2968
+ eurcAddress: 'HzwqbKZw8HxMN6bF2yFZNrht3c2iXXzpKcFu7uBEDKtr',
2969
+ usdcAddress: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
2970
+ cctp: {
2971
+ domain: 5,
2972
+ contracts: {
2973
+ v1: {
2974
+ type: 'split',
2975
+ tokenMessenger: 'CCTPiPYPc6AsJuwueEnWgSgucamXDZwBd53dQ11YiKX3',
2976
+ messageTransmitter: 'CCTPmbSD7gX1bxKPAmg77w8oFzNFpaQiQUWD43TKaecd',
2977
+ confirmations: 32,
2978
+ },
2979
+ v2: {
2980
+ type: 'split',
2981
+ tokenMessenger: 'CCTPV2vPZJS2u2BBsUoscuikbYjnpFmbFsvVuJdgUMQe',
2982
+ messageTransmitter: 'CCTPV2Sm4AdWt5296sk4P66VBZ7bEhcARwFaaS9YPbeC',
2983
+ confirmations: 32,
2984
+ fastConfirmations: 3,
2985
+ },
2986
+ },
2987
+ },
2988
+ kitContracts: {
2989
+ bridge: 'DFaauJEjmiHkPs1JG89A4p95hDWi9m9SAEERY1LQJiC3',
2990
+ },
2991
+ });
2992
+
2993
+ /**
2994
+ * Solana Devnet chain definition
2995
+ * @remarks
2996
+ * This represents the development test network for the Solana blockchain.
2997
+ */
2998
+ const SolanaDevnet = defineChain({
2999
+ type: 'solana',
3000
+ chain: Blockchain.Solana_Devnet,
3001
+ name: 'Solana Devnet',
3002
+ title: 'Solana Development Network',
3003
+ nativeCurrency: {
3004
+ name: 'Solana',
3005
+ symbol: 'SOL',
3006
+ decimals: 9,
3007
+ },
3008
+ isTestnet: true,
3009
+ explorerUrl: 'https://solscan.io/tx/{hash}?cluster=devnet',
3010
+ eurcAddress: 'HzwqbKZw8HxMN6bF2yFZNrht3c2iXXzpKcFu7uBEDKtr',
3011
+ usdcAddress: '4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU',
3012
+ cctp: {
3013
+ domain: 5,
3014
+ contracts: {
3015
+ v1: {
3016
+ type: 'split',
3017
+ tokenMessenger: 'CCTPiPYPc6AsJuwueEnWgSgucamXDZwBd53dQ11YiKX3',
3018
+ messageTransmitter: 'CCTPmbSD7gX1bxKPAmg77w8oFzNFpaQiQUWD43TKaecd',
3019
+ confirmations: 32,
3020
+ },
3021
+ v2: {
3022
+ type: 'split',
3023
+ tokenMessenger: 'CCTPV2vPZJS2u2BBsUoscuikbYjnpFmbFsvVuJdgUMQe',
3024
+ messageTransmitter: 'CCTPV2Sm4AdWt5296sk4P66VBZ7bEhcARwFaaS9YPbeC',
3025
+ confirmations: 32,
3026
+ fastConfirmations: 3,
3027
+ },
3028
+ },
3029
+ },
3030
+ kitContracts: {
3031
+ bridge: 'DFaauJEjmiHkPs1JG89A4p95hDWi9m9SAEERY1LQJiC3',
3032
+ },
3033
+ rpcEndpoints: ['https://api.devnet.solana.com'],
3034
+ });
3035
+
3036
+ /**
3037
+ * Stellar Mainnet chain definition
3038
+ * @remarks
3039
+ * This represents the official production network for the Stellar blockchain.
3040
+ */
3041
+ const Stellar = defineChain({
3042
+ type: 'stellar',
3043
+ chain: Blockchain.Stellar,
3044
+ name: 'Stellar',
3045
+ title: 'Stellar Mainnet',
3046
+ nativeCurrency: {
3047
+ name: 'Stellar Lumens',
3048
+ symbol: 'XLM',
3049
+ decimals: 7,
3050
+ },
3051
+ isTestnet: false,
3052
+ explorerUrl: 'https://stellar.expert/explorer/public/tx/{hash}',
3053
+ rpcEndpoints: ['https://horizon.stellar.org'],
3054
+ eurcAddress: 'EURC-GDHU6WRG4IEQXM5NZ4BMPKOXHW76MZM4Y2IEMFDVXBSDP6SJY4ITNPP2',
3055
+ usdcAddress: 'USDC-GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN',
3056
+ cctp: null,
3057
+ });
3058
+
3059
+ /**
3060
+ * Stellar Testnet chain definition
3061
+ * @remarks
3062
+ * This represents the official test network for the Stellar blockchain.
3063
+ */
3064
+ const StellarTestnet = defineChain({
3065
+ type: 'stellar',
3066
+ chain: Blockchain.Stellar_Testnet,
3067
+ name: 'Stellar Testnet',
3068
+ title: 'Stellar Test Network',
3069
+ nativeCurrency: {
3070
+ name: 'Stellar Lumens',
3071
+ symbol: 'XLM',
3072
+ decimals: 7,
3073
+ },
3074
+ isTestnet: true,
3075
+ explorerUrl: 'https://stellar.expert/explorer/testnet/tx/{hash}',
3076
+ rpcEndpoints: ['https://horizon-testnet.stellar.org'],
3077
+ eurcAddress: 'EURC-GB3Q6QDZYTHWT7E5PVS3W7FUT5GVAFC5KSZFFLPU25GO7VTC3NM2ZTVO',
3078
+ usdcAddress: 'USDC-GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5',
3079
+ cctp: null,
3080
+ });
3081
+
3082
+ /**
3083
+ * Sui Mainnet chain definition
3084
+ * @remarks
3085
+ * This represents the official production network for the Sui blockchain.
3086
+ */
3087
+ const Sui = defineChain({
3088
+ type: 'sui',
3089
+ chain: Blockchain.Sui,
3090
+ name: 'Sui',
3091
+ title: 'Sui Mainnet',
3092
+ nativeCurrency: {
3093
+ name: 'Sui',
3094
+ symbol: 'SUI',
3095
+ decimals: 9,
3096
+ },
3097
+ isTestnet: false,
3098
+ explorerUrl: 'https://suiscan.xyz/mainnet/tx/{hash}',
3099
+ rpcEndpoints: ['https://fullnode.mainnet.sui.io'],
3100
+ eurcAddress: null,
3101
+ usdcAddress: '0xdba34672e30cb065b1f93e3ab55318768fd6fef66c15942c9f7cb846e2f900e7::usdc::USDC',
3102
+ cctp: {
3103
+ domain: 8,
3104
+ contracts: {
3105
+ v1: {
3106
+ type: 'split',
3107
+ tokenMessenger: '0x2aa6c5d56376c371f88a6cc42e852824994993cb9bab8d3e6450cbe3cb32b94e',
3108
+ messageTransmitter: '0x08d87d37ba49e785dde270a83f8e979605b03dc552b5548f26fdf2f49bf7ed1b',
3109
+ confirmations: 1,
3110
+ },
3111
+ },
3112
+ },
3113
+ });
3114
+
3115
+ /**
3116
+ * Sui Testnet chain definition
3117
+ * @remarks
3118
+ * This represents the official test network for the Sui blockchain.
3119
+ */
3120
+ const SuiTestnet = defineChain({
3121
+ type: 'sui',
3122
+ chain: Blockchain.Sui_Testnet,
3123
+ name: 'Sui Testnet',
3124
+ title: 'Sui Test Network',
3125
+ nativeCurrency: {
3126
+ name: 'Sui',
3127
+ symbol: 'SUI',
3128
+ decimals: 9,
3129
+ },
3130
+ isTestnet: true,
3131
+ explorerUrl: 'https://suiscan.xyz/testnet/tx/{hash}',
3132
+ rpcEndpoints: ['https://fullnode.testnet.sui.io'],
3133
+ eurcAddress: null,
3134
+ usdcAddress: '0xa1ec7fc00a6f40db9693ad1415d0c193ad3906494428cf252621037bd7117e29::usdc::USDC',
3135
+ cctp: {
3136
+ domain: 8,
3137
+ contracts: {
3138
+ v1: {
3139
+ type: 'split',
3140
+ tokenMessenger: '0x31cc14d80c175ae39777c0238f20594c6d4869cfab199f40b69f3319956b8beb',
3141
+ messageTransmitter: '0x4931e06dce648b3931f890035bd196920770e913e43e45990b383f6486fdd0a5',
3142
+ confirmations: 1,
3143
+ },
3144
+ },
3145
+ },
3146
+ });
3147
+
3148
+ /**
3149
+ * Unichain Mainnet chain definition
3150
+ * @remarks
3151
+ * This represents the official production network for the Unichain blockchain.
3152
+ */
3153
+ const Unichain = defineChain({
3154
+ type: 'evm',
3155
+ chain: Blockchain.Unichain,
3156
+ name: 'Unichain',
3157
+ title: 'Unichain Mainnet',
3158
+ nativeCurrency: {
3159
+ name: 'Uni',
3160
+ symbol: 'UNI',
3161
+ decimals: 18,
3162
+ },
3163
+ chainId: 130,
3164
+ isTestnet: false,
3165
+ explorerUrl: 'https://unichain.blockscout.com/tx/{hash}',
3166
+ rpcEndpoints: ['https://rpc.unichain.org', 'https://mainnet.unichain.org'],
3167
+ eurcAddress: null,
3168
+ usdcAddress: '0x078D782b760474a361dDA0AF3839290b0EF57AD6',
3169
+ cctp: {
3170
+ domain: 10,
3171
+ contracts: {
3172
+ v1: {
3173
+ type: 'split',
3174
+ tokenMessenger: '0x4e744b28E787c3aD0e810eD65A24461D4ac5a762',
3175
+ messageTransmitter: '0x353bE9E2E38AB1D19104534e4edC21c643Df86f4',
3176
+ confirmations: 65,
3177
+ },
3178
+ v2: {
3179
+ type: 'split',
3180
+ tokenMessenger: '0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d',
3181
+ messageTransmitter: '0x81D40F21F12A8F0E3252Bccb954D722d4c464B64',
3182
+ confirmations: 65,
3183
+ fastConfirmations: 1,
3184
+ },
3185
+ },
3186
+ },
3187
+ kitContracts: {
3188
+ bridge: BRIDGE_CONTRACT_EVM_MAINNET,
3189
+ },
3190
+ });
3191
+
3192
+ /**
3193
+ * Unichain Sepolia Testnet chain definition
3194
+ * @remarks
3195
+ * This represents the official test network for the Unichain blockchain.
3196
+ */
3197
+ const UnichainSepolia = defineChain({
3198
+ type: 'evm',
3199
+ chain: Blockchain.Unichain_Sepolia,
3200
+ name: 'Unichain Sepolia',
3201
+ title: 'Unichain Sepolia Testnet',
3202
+ nativeCurrency: {
3203
+ name: 'Sepolia Uni',
3204
+ symbol: 'UNI',
3205
+ decimals: 18,
3206
+ },
3207
+ chainId: 1301,
3208
+ isTestnet: true,
3209
+ explorerUrl: 'https://unichain-sepolia.blockscout.com/tx/{hash}',
3210
+ rpcEndpoints: ['https://sepolia.unichain.org'],
3211
+ eurcAddress: null,
3212
+ usdcAddress: '0x31d0220469e10c4E71834a79b1f276d740d3768F',
3213
+ cctp: {
3214
+ domain: 10,
3215
+ contracts: {
3216
+ v1: {
3217
+ type: 'split',
3218
+ tokenMessenger: '0x8ed94B8dAd2Dc5453862ea5e316A8e71AAed9782',
3219
+ messageTransmitter: '0xbc498c326533d675cf571B90A2Ced265ACb7d086',
3220
+ confirmations: 65,
3221
+ },
3222
+ v2: {
3223
+ type: 'split',
3224
+ tokenMessenger: '0x8FE6B999Dc680CcFDD5Bf7EB0974218be2542DAA',
3225
+ messageTransmitter: '0xE737e5cEBEEBa77EFE34D4aa090756590b1CE275',
3226
+ confirmations: 65,
3227
+ fastConfirmations: 1,
3228
+ },
3229
+ },
3230
+ },
3231
+ kitContracts: {
3232
+ bridge: BRIDGE_CONTRACT_EVM_TESTNET,
3233
+ },
3234
+ });
3235
+
3236
+ /**
3237
+ * World Chain chain definition
3238
+ * @remarks
3239
+ * This represents the main network for the World Chain blockchain.
3240
+ */
3241
+ const WorldChain = defineChain({
3242
+ type: 'evm',
3243
+ chain: Blockchain.World_Chain,
3244
+ name: 'World Chain',
3245
+ title: 'World Chain',
3246
+ nativeCurrency: {
3247
+ name: 'Ether',
3248
+ symbol: 'ETH',
3249
+ decimals: 18,
3250
+ },
3251
+ chainId: 480,
3252
+ isTestnet: false,
3253
+ explorerUrl: 'https://worldscan.org/tx/{hash}',
3254
+ rpcEndpoints: ['https://worldchain-mainnet.g.alchemy.com/public'],
3255
+ eurcAddress: null,
3256
+ usdcAddress: '0x79A02482A880bCe3F13E09da970dC34dB4cD24D1',
3257
+ cctp: {
3258
+ domain: 14,
3259
+ contracts: {
3260
+ v2: {
3261
+ type: 'split',
3262
+ tokenMessenger: '0x28b5a0e9C621a5BadaA536219b3a228C8168cF5d',
3263
+ messageTransmitter: '0x81D40F21F12A8F0E3252Bccb954D722d4c464B64',
3264
+ confirmations: 65,
3265
+ fastConfirmations: 1,
3266
+ },
3267
+ },
3268
+ },
3269
+ kitContracts: {
3270
+ bridge: BRIDGE_CONTRACT_EVM_MAINNET,
3271
+ },
3272
+ });
3273
+
3274
+ /**
3275
+ * World Chain Sepolia chain definition
3276
+ * @remarks
3277
+ * This represents the test network for the World Chain blockchain.
3278
+ */
3279
+ const WorldChainSepolia = defineChain({
3280
+ type: 'evm',
3281
+ chain: Blockchain.World_Chain_Sepolia,
3282
+ name: 'World Chain Sepolia',
3283
+ title: 'World Chain Sepolia',
3284
+ nativeCurrency: {
3285
+ name: 'Ether',
3286
+ symbol: 'ETH',
3287
+ decimals: 18,
3288
+ },
3289
+ chainId: 4801,
3290
+ isTestnet: true,
3291
+ explorerUrl: 'https://sepolia.worldscan.org/tx/{hash}',
3292
+ rpcEndpoints: [
3293
+ 'https://worldchain-sepolia.drpc.org',
3294
+ 'https://worldchain-sepolia.g.alchemy.com/public',
3295
+ ],
3296
+ eurcAddress: null,
3297
+ usdcAddress: '0x66145f38cBAC35Ca6F1Dfb4914dF98F1614aeA88',
3298
+ cctp: {
3299
+ domain: 14,
3300
+ contracts: {
3301
+ v2: {
3302
+ type: 'split',
3303
+ tokenMessenger: '0x8fe6b999dc680ccfdd5bf7eb0974218be2542daa',
3304
+ messageTransmitter: '0xe737e5cebeeba77efe34d4aa090756590b1ce275',
3305
+ confirmations: 65,
3306
+ fastConfirmations: 1,
3307
+ },
3308
+ },
3309
+ },
3310
+ kitContracts: {
3311
+ bridge: BRIDGE_CONTRACT_EVM_TESTNET,
3312
+ },
3313
+ });
3314
+
3315
+ /**
3316
+ * XDC Mainnet chain definition
3317
+ * @remarks
3318
+ * This represents the official production network for the XDC blockchain.
3319
+ * XDC is a Layer 1 blockchain specialized for DeFi and trading applications
3320
+ * with native orderbook and matching engine.
3321
+ */
3322
+ const XDC = defineChain({
3323
+ type: 'evm',
3324
+ chain: Blockchain.XDC,
3325
+ name: 'XDC',
3326
+ title: 'XDC Mainnet',
3327
+ nativeCurrency: {
3328
+ name: 'XDC',
3329
+ symbol: 'XDC',
3330
+ decimals: 18,
3331
+ },
3332
+ chainId: 50,
3333
+ isTestnet: false,
3334
+ explorerUrl: 'https://xdcscan.io/tx/{hash}',
3335
+ rpcEndpoints: ['https://erpc.xinfin.network'],
3336
+ eurcAddress: null,
3337
+ usdcAddress: '0xfA2958CB79b0491CC627c1557F441eF849Ca8eb1',
3338
+ cctp: {
3339
+ domain: 18,
3340
+ contracts: {
3341
+ v2: {
3342
+ type: 'split',
3343
+ tokenMessenger: '0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d',
3344
+ messageTransmitter: '0x81D40F21F12A8F0E3252Bccb954D722d4c464B64',
3345
+ confirmations: 3,
3346
+ fastConfirmations: 3,
3347
+ },
3348
+ },
3349
+ },
3350
+ kitContracts: {
3351
+ bridge: BRIDGE_CONTRACT_EVM_MAINNET,
3352
+ },
3353
+ });
3354
+
3355
+ /**
3356
+ * XDC Apothem Testnet chain definition
3357
+ * @remarks
3358
+ * This represents the official test network for the XDC Network, known as Apothem.
3359
+ */
3360
+ const XDCApothem = defineChain({
3361
+ type: 'evm',
3362
+ chain: Blockchain.XDC_Apothem,
3363
+ name: 'Apothem Network',
3364
+ title: 'Apothem Network',
3365
+ nativeCurrency: {
3366
+ name: 'TXDC',
3367
+ symbol: 'TXDC',
3368
+ decimals: 18,
3369
+ },
3370
+ chainId: 51,
3371
+ isTestnet: true,
3372
+ explorerUrl: 'https://testnet.xdcscan.com/tx/{hash}',
3373
+ rpcEndpoints: ['https://erpc.apothem.network'],
3374
+ eurcAddress: null,
3375
+ usdcAddress: '0xb5AB69F7bBada22B28e79C8FFAECe55eF1c771D4',
3376
+ cctp: {
3377
+ domain: 18,
3378
+ contracts: {
3379
+ v2: {
3380
+ type: 'split',
3381
+ tokenMessenger: '0x8FE6B999Dc680CcFDD5Bf7EB0974218be2542DAA',
3382
+ messageTransmitter: '0xE737e5cEBEEBa77EFE34D4aa090756590b1CE275',
3383
+ confirmations: 3,
3384
+ fastConfirmations: 1,
3385
+ },
3386
+ },
3387
+ },
3388
+ kitContracts: {
3389
+ bridge: BRIDGE_CONTRACT_EVM_TESTNET,
3390
+ },
3391
+ });
3392
+
3393
+ /**
3394
+ * ZKSync Era Mainnet chain definition
3395
+ * @remarks
3396
+ * This represents the official production network for the ZKSync Era blockchain.
3397
+ */
3398
+ const ZKSyncEra = defineChain({
3399
+ type: 'evm',
3400
+ chain: Blockchain.ZKSync_Era,
3401
+ name: 'ZKSync Era',
3402
+ title: 'ZKSync Era Mainnet',
3403
+ nativeCurrency: {
3404
+ name: 'Ether',
3405
+ symbol: 'ETH',
3406
+ decimals: 18,
3407
+ },
3408
+ chainId: 324,
3409
+ isTestnet: false,
3410
+ explorerUrl: 'https://explorer.zksync.io/tx/{hash}',
3411
+ rpcEndpoints: ['https://mainnet.era.zksync.io'],
3412
+ eurcAddress: null,
3413
+ usdcAddress: '0x1d17CBcF0D6D143135aE902365D2E5e2A16538D4',
3414
+ cctp: null,
3415
+ });
3416
+
3417
+ /**
3418
+ * ZKSync Era Sepolia Testnet chain definition
3419
+ * @remarks
3420
+ * This represents the official test network for the ZKSync Era blockchain on Sepolia.
3421
+ */
3422
+ const ZKSyncEraSepolia = defineChain({
3423
+ type: 'evm',
3424
+ chain: Blockchain.ZKSync_Sepolia,
3425
+ name: 'ZKSync Era Sepolia',
3426
+ title: 'ZKSync Era Sepolia Testnet',
3427
+ nativeCurrency: {
3428
+ name: 'Sepolia Ether',
3429
+ symbol: 'ETH',
3430
+ decimals: 18,
3431
+ },
3432
+ chainId: 300,
3433
+ isTestnet: true,
3434
+ explorerUrl: 'https://sepolia.explorer.zksync.io/tx/{hash}',
3435
+ rpcEndpoints: ['https://sepolia.era.zksync.dev'],
3436
+ eurcAddress: null,
3437
+ usdcAddress: '0xAe045DE5638162fa134807Cb558E15A3F5A7F853',
3438
+ cctp: null,
3439
+ });
3440
+
3441
+ var Blockchains = /*#__PURE__*/Object.freeze({
3442
+ __proto__: null,
3443
+ Algorand: Algorand,
3444
+ AlgorandTestnet: AlgorandTestnet,
3445
+ Aptos: Aptos,
3446
+ AptosTestnet: AptosTestnet,
3447
+ Arbitrum: Arbitrum,
3448
+ ArbitrumSepolia: ArbitrumSepolia,
3449
+ Avalanche: Avalanche,
3450
+ AvalancheFuji: AvalancheFuji,
3451
+ Base: Base,
3452
+ BaseSepolia: BaseSepolia,
3453
+ Celo: Celo,
3454
+ CeloAlfajoresTestnet: CeloAlfajoresTestnet,
3455
+ Codex: Codex,
3456
+ CodexTestnet: CodexTestnet,
3457
+ Ethereum: Ethereum,
3458
+ EthereumSepolia: EthereumSepolia,
3459
+ Hedera: Hedera,
3460
+ HederaTestnet: HederaTestnet,
3461
+ HyperEVM: HyperEVM,
3462
+ HyperEVMTestnet: HyperEVMTestnet,
3463
+ Ink: Ink,
3464
+ InkTestnet: InkTestnet,
3465
+ Linea: Linea,
3466
+ LineaSepolia: LineaSepolia,
3467
+ NEAR: NEAR,
3468
+ NEARTestnet: NEARTestnet,
3469
+ Noble: Noble,
3470
+ NobleTestnet: NobleTestnet,
3471
+ Optimism: Optimism,
3472
+ OptimismSepolia: OptimismSepolia,
3473
+ Plume: Plume,
3474
+ PlumeTestnet: PlumeTestnet,
3475
+ PolkadotAssetHub: PolkadotAssetHub,
3476
+ PolkadotWestmint: PolkadotWestmint,
3477
+ Polygon: Polygon,
3478
+ PolygonAmoy: PolygonAmoy,
3479
+ Sei: Sei,
3480
+ SeiTestnet: SeiTestnet,
3481
+ Solana: Solana,
3482
+ SolanaDevnet: SolanaDevnet,
3483
+ Sonic: Sonic,
3484
+ SonicTestnet: SonicTestnet,
3485
+ Stellar: Stellar,
3486
+ StellarTestnet: StellarTestnet,
3487
+ Sui: Sui,
3488
+ SuiTestnet: SuiTestnet,
3489
+ Unichain: Unichain,
3490
+ UnichainSepolia: UnichainSepolia,
3491
+ WorldChain: WorldChain,
3492
+ WorldChainSepolia: WorldChainSepolia,
3493
+ XDC: XDC,
3494
+ XDCApothem: XDCApothem,
3495
+ ZKSyncEra: ZKSyncEra,
3496
+ ZKSyncEraSepolia: ZKSyncEraSepolia
3497
+ });
3498
+
3499
+ /**
3500
+ * Base schema for common chain definition properties.
3501
+ * This contains all properties shared between EVM and non-EVM chains.
3502
+ */
3503
+ const baseChainDefinitionSchema = z.object({
3504
+ chain: z.nativeEnum(Blockchain, {
3505
+ required_error: 'Chain enum is required. Please provide a valid Blockchain enum value.',
3506
+ invalid_type_error: 'Chain must be a valid Blockchain enum value.',
3507
+ }),
3508
+ name: z.string({
3509
+ required_error: 'Chain name is required. Please provide a valid chain name.',
3510
+ invalid_type_error: 'Chain name must be a string.',
3511
+ }),
3512
+ title: z.string().optional(),
3513
+ nativeCurrency: z.object({
3514
+ name: z.string(),
3515
+ symbol: z.string(),
3516
+ decimals: z.number(),
3517
+ }),
3518
+ isTestnet: z.boolean({
3519
+ required_error: 'isTestnet is required. Please specify whether this is a testnet.',
3520
+ invalid_type_error: 'isTestnet must be a boolean.',
3521
+ }),
3522
+ explorerUrl: z.string({
3523
+ required_error: 'Explorer URL is required. Please provide a valid explorer URL.',
3524
+ invalid_type_error: 'Explorer URL must be a string.',
3525
+ }),
3526
+ rpcEndpoints: z.array(z.string()),
3527
+ eurcAddress: z.string().nullable(),
3528
+ usdcAddress: z.string().nullable(),
3529
+ cctp: z.any().nullable(), // We'll accept any CCTP config structure
3530
+ kitContracts: z
3531
+ .object({
3532
+ bridge: z.string().optional(),
3533
+ })
3534
+ .optional(),
3535
+ });
3536
+ /**
3537
+ * Zod schema for validating EVM chain definitions specifically.
3538
+ * This schema extends the base schema with EVM-specific properties.
3539
+ *
3540
+ * @example
3541
+ * ```typescript
3542
+ * import { evmChainDefinitionSchema } from '@core/chains/validation'
3543
+ * import { Blockchain } from '@core/chains'
3544
+ *
3545
+ * const ethereumChain = {
3546
+ * type: 'evm',
3547
+ * chain: Blockchain.Ethereum,
3548
+ * name: 'Ethereum',
3549
+ * chainId: 1,
3550
+ * nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 },
3551
+ * isTestnet: false,
3552
+ * explorerUrl: 'https://etherscan.io/tx/{hash}',
3553
+ * usdcAddress: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48',
3554
+ * eurcAddress: null,
3555
+ * cctp: null
3556
+ * }
3557
+ *
3558
+ * const result = evmChainDefinitionSchema.safeParse(ethereumChain)
3559
+ * if (result.success) {
3560
+ * console.log('EVM chain definition is valid')
3561
+ * } else {
3562
+ * console.error('Validation failed:', result.error)
3563
+ * }
3564
+ * ```
3565
+ */
3566
+ const evmChainDefinitionSchema = baseChainDefinitionSchema
3567
+ .extend({
3568
+ type: z.literal('evm'),
3569
+ chainId: z.number({
3570
+ required_error: 'EVM chains must have a chainId. Please provide a valid EVM chain ID.',
3571
+ invalid_type_error: 'EVM chain ID must be a number.',
3572
+ }),
3573
+ })
3574
+ .strict(); //// Reject any additional properties not defined in the schema
3575
+ /**
3576
+ * Zod schema for validating non-EVM chain definitions.
3577
+ * This schema extends the base schema with non-EVM specific properties.
3578
+ */
3579
+ const nonEvmChainDefinitionSchema = baseChainDefinitionSchema
3580
+ .extend({
3581
+ type: z.enum([
3582
+ 'algorand',
3583
+ 'avalanche',
3584
+ 'solana',
3585
+ 'aptos',
3586
+ 'near',
3587
+ 'stellar',
3588
+ 'sui',
3589
+ 'hedera',
3590
+ 'noble',
3591
+ 'polkadot',
3592
+ ]),
3593
+ })
3594
+ .strict(); // Reject any additional properties not defined in the schema
3595
+ /**
3596
+ * Discriminated union schema for all chain definitions.
3597
+ * This schema validates different chain types based on their 'type' field.
3598
+ *
3599
+ * @example
3600
+ * ```typescript
3601
+ * import { chainDefinitionSchema } from '@core/chains/validation'
3602
+ * import { Blockchain } from '@core/chains'
3603
+ *
3604
+ * // EVM chain
3605
+ * chainDefinitionSchema.parse({
3606
+ * type: 'evm',
3607
+ * chain: Blockchain.Ethereum,
3608
+ * chainId: 1,
3609
+ * // ... other properties
3610
+ * })
3611
+ *
3612
+ * // Non-EVM chain
3613
+ * chainDefinitionSchema.parse({
3614
+ * type: 'solana',
3615
+ * chain: Blockchain.Solana,
3616
+ * // ... other properties (no chainId)
3617
+ * })
3618
+ * ```
3619
+ */
3620
+ const chainDefinitionSchema = z.discriminatedUnion('type', [
3621
+ evmChainDefinitionSchema,
3622
+ nonEvmChainDefinitionSchema,
3623
+ ]);
3624
+ /**
3625
+ * Zod schema for validating chain identifiers.
3626
+ * This schema accepts either a string blockchain identifier, a Blockchain enum value,
3627
+ * or a full ChainDefinition object.
3628
+ *
3629
+ * @example
3630
+ * ```typescript
3631
+ * import { chainIdentifierSchema } from '@core/chains/validation'
3632
+ * import { Blockchain, Ethereum } from '@core/chains'
3633
+ *
3634
+ * // All of these are valid:
3635
+ * chainIdentifierSchema.parse('Ethereum')
3636
+ * chainIdentifierSchema.parse(Blockchain.Ethereum)
3637
+ * chainIdentifierSchema.parse(Ethereum)
3638
+ * ```
3639
+ */
3640
+ const chainIdentifierSchema = z.union([
3641
+ z
3642
+ .string()
3643
+ .refine((val) => val in Blockchain, 'Must be a valid Blockchain enum value as string'),
3644
+ z.nativeEnum(Blockchain),
3645
+ chainDefinitionSchema,
3646
+ ]);
3647
+
3648
+ /**
3649
+ * Retrieve a chain definition by its blockchain enum value.
3650
+ *
3651
+ * Searches the set of known chain definitions and returns the one matching the provided
3652
+ * blockchain enum or string value. Throws an error if no matching chain is found.
3653
+ *
3654
+ * @param blockchain - The blockchain enum or its string representation to look up.
3655
+ * @returns The corresponding ChainDefinition object for the given blockchain.
3656
+ *
3657
+ * @throws Error If no chain definition is found for the provided enum value.
3658
+ *
3659
+ * @example
3660
+ * ```typescript
3661
+ * import { getChainByEnum } from '@core/chains'
3662
+ * import { Blockchain } from '@core/chains'
3663
+ *
3664
+ * const ethereum = getChainByEnum(Blockchain.Ethereum)
3665
+ * console.log(ethereum.name) // "Ethereum"
3666
+ * ```
3667
+ */
3668
+ const getChainByEnum = (blockchain) => {
3669
+ const chain = Object.values(Blockchains).find((chain) => {
3670
+ return chain.chain === blockchain;
3671
+ });
3672
+ if (!chain) {
3673
+ throw new Error(`No chain definition found for blockchain: ${blockchain}`);
3674
+ }
3675
+ return chain;
3676
+ };
3677
+
3678
+ /**
3679
+ * Resolves a flexible chain identifier to a ChainDefinition.
3680
+ *
3681
+ * This function handles all three supported formats:
3682
+ * - ChainDefinition objects (passed through unchanged)
3683
+ * - Blockchain enum values (resolved via getChainByEnum)
3684
+ * - String literals of blockchain values (resolved via getChainByEnum)
3685
+ *
3686
+ * @param chainIdentifier - The chain identifier to resolve
3687
+ * @returns The resolved ChainDefinition object
3688
+ * @throws Error if the chain identifier cannot be resolved
3689
+ *
3690
+ * @example
3691
+ * ```typescript
3692
+ * import { resolveChainIdentifier } from '@core/chains'
3693
+ * import { Blockchain, Ethereum } from '@core/chains'
3694
+ *
3695
+ * // All of these resolve to the same ChainDefinition:
3696
+ * const chain1 = resolveChainIdentifier(Ethereum)
3697
+ * const chain2 = resolveChainIdentifier(Blockchain.Ethereum)
3698
+ * const chain3 = resolveChainIdentifier('Ethereum')
3699
+ * ```
3700
+ */
3701
+ function resolveChainIdentifier(chainIdentifier) {
3702
+ // If it's already a ChainDefinition object, return it unchanged
3703
+ if (typeof chainIdentifier === 'object') {
3704
+ return chainIdentifier;
3705
+ }
3706
+ // If it's a string or enum value, resolve it via getChainByEnum
3707
+ if (typeof chainIdentifier === 'string') {
3708
+ return getChainByEnum(chainIdentifier);
3709
+ }
3710
+ // This should never happen with proper typing, but provide a fallback
3711
+ throw new Error(`Invalid chain identifier type: ${typeof chainIdentifier}. Expected ChainDefinition object, Blockchain enum, or string literal.`);
3712
+ }
3713
+
3714
+ /**
3715
+ * Schema for validating hexadecimal strings with '0x' prefix.
3716
+ *
3717
+ * This schema validates that a string:
3718
+ * - Is a string type
3719
+ * - Is not empty after trimming
3720
+ * - Starts with '0x'
3721
+ * - Contains only valid hexadecimal characters (0-9, a-f, A-F) after '0x'
3722
+ *
3723
+ * @remarks
3724
+ * This schema does not validate length, making it suitable for various hex string types
3725
+ * like addresses, transaction hashes, and other hex-encoded data.
3726
+ *
3727
+ * @throws {ValidationError} If validation fails, with details about which properties failed
3728
+ *
3729
+ * @example
3730
+ * ```typescript
3731
+ * import { hexStringSchema } from '@core/adapter'
3732
+ *
3733
+ * const validAddress = '0x1234567890123456789012345678901234567890'
3734
+ * const validTxHash = '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef'
3735
+ *
3736
+ * const addressResult = hexStringSchema.safeParse(validAddress)
3737
+ * const txHashResult = hexStringSchema.safeParse(validTxHash)
3738
+ * console.log(addressResult.success) // true
3739
+ * console.log(txHashResult.success) // true
3740
+ * ```
3741
+ */
3742
+ const hexStringSchema = z
3743
+ .string()
3744
+ .min(1, 'Hex string is required')
3745
+ .refine((value) => value.trim().length > 0, 'Hex string cannot be empty')
3746
+ .refine((value) => value.startsWith('0x'), 'Hex string must start with 0x prefix')
3747
+ .refine((value) => {
3748
+ const hexPattern = /^0x[0-9a-fA-F]+$/;
3749
+ return hexPattern.test(value);
3750
+ }, 'Hex string contains invalid characters. Only hexadecimal characters (0-9, a-f, A-F) are allowed after 0x');
3751
+ /**
3752
+ * Schema for validating EVM addresses.
3753
+ *
3754
+ * This schema validates that a string is a properly formatted EVM address:
3755
+ * - Must be a valid hex string with '0x' prefix
3756
+ * - Must be exactly 42 characters long (0x + 40 hex characters)
3757
+ *
3758
+ * @throws {ValidationError} If validation fails, with details about which properties failed
3759
+ *
3760
+ * @example
3761
+ * ```typescript
3762
+ * import { evmAddressSchema } from '@core/adapter'
3763
+ *
3764
+ * const validAddress = '0x1234567890123456789012345678901234567890'
3765
+ *
3766
+ * const result = evmAddressSchema.safeParse(validAddress)
3767
+ * console.log(result.success) // true
3768
+ * ```
3769
+ */
3770
+ hexStringSchema.refine((value) => value.length === 42, 'EVM address must be exactly 42 characters long (0x + 40 hex characters)');
3771
+ /**
3772
+ * Schema for validating transaction hashes.
3773
+ *
3774
+ * This schema validates that a string is a properly formatted transaction hash:
3775
+ * - Must be a valid hex string with '0x' prefix
3776
+ * - Must be exactly 66 characters long (0x + 64 hex characters)
3777
+ *
3778
+ * @throws {ValidationError} If validation fails, with details about which properties failed
3779
+ *
3780
+ * @example
3781
+ * ```typescript
3782
+ * import { evmTransactionHashSchema } from '@core/adapter'
3783
+ *
3784
+ * const validTxHash = '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef'
3785
+ *
3786
+ * const result = evmTransactionHashSchema.safeParse(validTxHash)
3787
+ * console.log(result.success) // true
3788
+ * ```
3789
+ */
3790
+ hexStringSchema.refine((value) => value.length === 66, 'Transaction hash must be exactly 66 characters long (0x + 64 hex characters)');
3791
+ /**
3792
+ * Schema for validating base58-encoded strings.
3793
+ *
3794
+ * This schema validates that a string:
3795
+ * - Is a string type
3796
+ * - Is not empty after trimming
3797
+ * - Contains only valid base58 characters (1-9, A-H, J-N, P-Z, a-k, m-z)
3798
+ * - Does not contain commonly confused characters (0, O, I, l)
3799
+ *
3800
+ * @remarks
3801
+ * This schema does not validate length, making it suitable for various base58-encoded data
3802
+ * like Solana addresses, transaction signatures, and other base58-encoded data.
3803
+ *
3804
+ * @throws {ValidationError} If validation fails, with details about which properties failed
3805
+ *
3806
+ * @example
3807
+ * ```typescript
3808
+ * import { base58StringSchema } from '@core/adapter'
3809
+ *
3810
+ * const validAddress = 'DhzPkKCLJGHBZbs1AzmK2tRNLZkV8J3yWF3LuWMuKJpN'
3811
+ * const validTxHash = '3Jf8k2L5mN9pQ7rS1tV4wX6yZ8aB2cD4eF5gH7iJ9kL1mN3oP5qR7sT9uV1wX3yZ5'
3812
+ *
3813
+ * const addressResult = base58StringSchema.safeParse(validAddress)
3814
+ * const txHashResult = base58StringSchema.safeParse(validTxHash)
3815
+ * console.log(addressResult.success) // true
3816
+ * console.log(txHashResult.success) // true
3817
+ * ```
3818
+ */
3819
+ const base58StringSchema = z
3820
+ .string()
3821
+ .min(1, 'Base58 string is required')
3822
+ .refine((value) => value.trim().length > 0, 'Base58 string cannot be empty')
3823
+ .refine((value) => {
3824
+ // Base58 alphabet: 123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz
3825
+ // Excludes: 0, O, I, l to avoid confusion
3826
+ const base58Pattern = /^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]+$/;
3827
+ return base58Pattern.test(value);
3828
+ }, 'Base58 string contains invalid characters. Only base58 characters (1-9, A-H, J-N, P-Z, a-k, m-z) are allowed');
3829
+ /**
3830
+ * Schema for validating Solana addresses.
3831
+ *
3832
+ * This schema validates that a string is a properly formatted Solana address:
3833
+ * - Must be a valid base58-encoded string
3834
+ * - Must be between 32-44 characters long (typical length for base58-encoded 32-byte addresses)
3835
+ *
3836
+ * @throws {ValidationError} If validation fails, with details about which properties failed
3837
+ *
3838
+ * @example
3839
+ * ```typescript
3840
+ * import { solanaAddressSchema } from '@core/adapter'
3841
+ *
3842
+ * const validAddress = 'DhzPkKCLJGHBZbs1AzmK2tRNLZkV8J3yWF3LuWMuKJpN'
3843
+ *
3844
+ * const result = solanaAddressSchema.safeParse(validAddress)
3845
+ * console.log(result.success) // true
3846
+ * ```
3847
+ */
3848
+ base58StringSchema.refine((value) => value.length >= 32 && value.length <= 44, 'Solana address must be between 32-44 characters long (base58-encoded 32-byte address)');
3849
+ /**
3850
+ * Schema for validating Solana transaction hashes.
3851
+ *
3852
+ * This schema validates that a string is a properly formatted Solana transaction hash:
3853
+ * - Must be a valid base58-encoded string
3854
+ * - Must be between 86-88 characters long (typical length for base58-encoded 64-byte signatures)
3855
+ *
3856
+ * @throws {ValidationError} If validation fails, with details about which properties failed
3857
+ *
3858
+ * @example
3859
+ * ```typescript
3860
+ * import { solanaTransactionHashSchema } from '@core/adapter'
3861
+ *
3862
+ * const validTxHash = '5VfYmGBjvQKe3xgLtTQPSMEUdpEVHrJwLK7pKBJWKzYpNBE2g3kJrq7RSe9M8DqzQJ5J2aZPTjHLvd4WgxPpJKS'
3863
+ *
3864
+ * const result = solanaTransactionHashSchema.safeParse(validTxHash)
3865
+ * console.log(result.success) // true
3866
+ * ```
3867
+ */
3868
+ base58StringSchema.refine((value) => value.length >= 86 && value.length <= 88, 'Solana transaction hash must be between 86-88 characters long (base58-encoded 64-byte signature)');
3869
+ /**
3870
+ * Schema for validating Adapter objects.
3871
+ * Checks for the required methods that define an Adapter.
3872
+ */
3873
+ const adapterSchema = z.object({
3874
+ prepare: z.function(),
3875
+ waitForTransaction: z.function(),
3876
+ getAddress: z.function(),
3877
+ });
3878
+
3879
+ /**
3880
+ * Transfer speed options for cross-chain operations.
3881
+ *
3882
+ * Defines the available speed modes for CCTPv2 transfers, affecting
3883
+ * both transfer time and potential fee implications.
3884
+ */
3885
+ var TransferSpeed;
3886
+ (function (TransferSpeed) {
3887
+ /** Fast burn mode - reduces transfer time but may have different fee implications */
3888
+ TransferSpeed["FAST"] = "FAST";
3889
+ /** Standard burn mode - normal transfer time with standard fees */
3890
+ TransferSpeed["SLOW"] = "SLOW";
3891
+ })(TransferSpeed || (TransferSpeed = {}));
3892
+
3893
+ /**
3894
+ * Factory to validate a numeric string that may include thousand separators and decimal part.
3895
+ * Supports either comma or dot as decimal separator and the other as thousands.
3896
+ *
3897
+ * Behavior differences controlled by options:
3898
+ * - allowZero: when false, value must be strictly greater than 0; when true, non-negative.
3899
+ * - regexMessage: error message when the basic numeric format fails.
3900
+ */
3901
+ const createDecimalStringValidator = (options) => (schema) => schema
3902
+ .regex(/^\d+(?:[.,]\d{3})*(?:[.,]\d+)?$/, options.regexMessage)
3903
+ .superRefine((val, ctx) => {
3904
+ const lastSeparator = val.lastIndexOf(',');
3905
+ const lastDot = val.lastIndexOf('.');
3906
+ const isCommaSeparated = lastSeparator > lastDot;
3907
+ const normalizedValue = val
3908
+ .replace(isCommaSeparated ? /\./g : /,/g, '')
3909
+ .replace(isCommaSeparated ? ',' : '.', '.');
3910
+ const amount = parseFloat(normalizedValue);
3911
+ if (Number.isNaN(amount)) {
3912
+ ctx.addIssue({
3913
+ code: z.ZodIssueCode.custom,
3914
+ message: options.regexMessage,
3915
+ });
3916
+ return;
3917
+ }
3918
+ // Check decimal precision if maxDecimals is specified
3919
+ if (options.maxDecimals !== undefined) {
3920
+ const decimalPart = normalizedValue.split('.')[1];
3921
+ if (decimalPart && decimalPart.length > options.maxDecimals) {
3922
+ ctx.addIssue({
3923
+ code: z.ZodIssueCode.custom,
3924
+ message: `Maximum supported decimal places: ${options.maxDecimals.toString()}`,
3925
+ });
3926
+ return;
3927
+ }
3928
+ }
3929
+ if (options.allowZero && amount < 0) {
3930
+ ctx.addIssue({
3931
+ code: z.ZodIssueCode.custom,
3932
+ message: `${options.attributeName} must be non-negative`,
3933
+ });
3934
+ }
3935
+ else if (!options.allowZero && amount <= 0) {
3936
+ ctx.addIssue({
3937
+ code: z.ZodIssueCode.custom,
3938
+ message: `${options.attributeName} must be greater than 0`,
3939
+ });
3940
+ }
3941
+ });
3942
+ /**
3943
+ * Schema for validating chain definitions.
3944
+ * This ensures the basic structure of a chain definition is valid.
3945
+ * A chain definition must include at minimum a name and type.
3946
+ *
3947
+ * @throws ValidationError If validation fails, with details about which properties failed
3948
+ *
3949
+ * @example
3950
+ * ```typescript
3951
+ * import { chainDefinitionSchema } from '@core/provider'
3952
+ *
3953
+ * const validChain = {
3954
+ * name: 'Ethereum',
3955
+ * type: 'evm'
3956
+ * }
3957
+ *
3958
+ * const result = chainDefinitionSchema.safeParse(validChain)
3959
+ * console.log(result.success) // true
3960
+ * ```
3961
+ */
3962
+ z.object({
3963
+ name: z.string().min(1, 'Chain name is required'),
3964
+ type: z.string().min(1, 'Chain type is required'),
3965
+ });
3966
+ /**
3967
+ * Schema for validating wallet contexts.
3968
+ * This ensures all required fields are present and properly typed.
3969
+ * A wallet context must include:
3970
+ * - A valid adapter with prepare and execute methods
3971
+ * - A valid Ethereum address
3972
+ * - A valid chain definition with required properties
3973
+ *
3974
+ * @throws ValidationError If validation fails, with details about which properties failed
3975
+ *
3976
+ * @example
3977
+ * ```typescript
3978
+ * import { walletContextSchema } from '@core/provider'
3979
+ *
3980
+ * const validContext = {
3981
+ * adapter: {
3982
+ * prepare: () => Promise.resolve({}),
3983
+ * waitForTransaction: () => Promise.resolve({})
3984
+ * },
3985
+ * address: '0x1234567890123456789012345678901234567890',
3986
+ * chain: {
3987
+ * name: 'Ethereum',
3988
+ * type: 'evm',
3989
+ * isTestnet: false
3990
+ * }
3991
+ * }
3992
+ *
3993
+ * const result = walletContextSchema.safeParse(validContext)
3994
+ * console.log(result.success) // true
3995
+ * ```
3996
+ */
3997
+ const walletContextSchema = z.object({
3998
+ adapter: z.object({
3999
+ prepare: z.function().returns(z.any()),
4000
+ waitForTransaction: z.function().returns(z.any()),
4001
+ }),
4002
+ address: z.string().min(1),
4003
+ chain: z.object({
4004
+ name: z.string(),
4005
+ type: z.string(),
4006
+ isTestnet: z.boolean(),
4007
+ }),
4008
+ });
4009
+ /**
4010
+ * Schema for validating a custom fee configuration.
4011
+ * Validates the simplified CustomFee interface which includes:
4012
+ * - An optional fee amount as string
4013
+ * - An optional fee recipient as string address
4014
+ *
4015
+ * @throws ValidationError If validation fails
4016
+ *
4017
+ * @example
4018
+ * ```typescript
4019
+ * import { customFeeSchema } from '@core/provider'
4020
+ *
4021
+ * const validConfig = {
4022
+ * value: '1000000',
4023
+ * recipientAddress: '0x1234567890123456789012345678901234567890'
4024
+ * }
4025
+ *
4026
+ * const result = customFeeSchema.safeParse(validConfig)
4027
+ * console.log(result.success) // true
4028
+ * ```
4029
+ */
4030
+ const customFeeSchema = z
4031
+ .object({
4032
+ /**
4033
+ * The fee to charge for the transfer as string.
4034
+ * Must be a non-negative value.
4035
+ */
4036
+ value: createDecimalStringValidator({
4037
+ allowZero: true,
4038
+ regexMessage: 'Value must be non-negative',
4039
+ attributeName: 'value',
4040
+ })(z.string()).optional(),
4041
+ /**
4042
+ * The fee recipient address.
4043
+ * Must be a valid address string.
4044
+ */
4045
+ recipientAddress: z
4046
+ .string()
4047
+ .trim()
4048
+ .min(1, 'Fee recipient must be a non-empty string')
4049
+ .optional(),
4050
+ })
4051
+ .strict();
4052
+ /**
4053
+ * Schema for validating bridge parameters.
4054
+ * This ensures all required fields are present and properly typed.
4055
+ * A bridge must include:
4056
+ * - A valid amount (non-empty numeric string \> 0)
4057
+ * - Valid source and destination wallet contexts
4058
+ * - USDC as the token
4059
+ * - Optional config with transfer speed and max fee settings
4060
+ *
4061
+ * @throws ValidationError If validation fails, with details about which properties failed
4062
+ *
4063
+ * @example
4064
+ * ```typescript
4065
+ * import { bridgeParamsSchema } from '@core/provider'
4066
+ *
4067
+ * const validBridge = {
4068
+ * amount: '100.50',
4069
+ * source: {
4070
+ * adapter: sourceAdapter,
4071
+ * address: '0xSourceAddress',
4072
+ * chain: sourceChain
4073
+ * },
4074
+ * destination: {
4075
+ * adapter: destAdapter,
4076
+ * address: '0xDestAddress',
4077
+ * chain: destChain
4078
+ * },
4079
+ * token: 'USDC',
4080
+ * config: {
4081
+ * transferSpeed: 'FAST',
4082
+ * maxFee: '1000000'
4083
+ * customFee: {
4084
+ * value: '1000000',
4085
+ * recipientAddress: '0x1234567890123456789012345678901234567890'
4086
+ * }
4087
+ * }
4088
+ * }
4089
+ *
4090
+ * const result = bridgeParamsSchema.safeParse(validBridge)
4091
+ * console.log(result.success) // true
4092
+ * ```
4093
+ */
4094
+ z.object({
4095
+ amount: z
4096
+ .string()
4097
+ .min(1, 'Required')
4098
+ .pipe(createDecimalStringValidator({
4099
+ allowZero: false,
4100
+ regexMessage: 'Amount must be a numeric string with optional decimal places (e.g., 10.5, 10,5, 1.000,50 or 1,000.50)',
4101
+ attributeName: 'amount',
4102
+ maxDecimals: 6,
4103
+ })(z.string())),
4104
+ source: walletContextSchema,
4105
+ destination: walletContextSchema,
4106
+ token: z.literal('USDC'),
4107
+ config: z.object({
4108
+ transferSpeed: z.nativeEnum(TransferSpeed).optional(),
4109
+ maxFee: z
4110
+ .string()
4111
+ .regex(/^\d+$/, 'Max fee must be a numeric string')
4112
+ .optional(),
4113
+ customFee: customFeeSchema.optional(),
4114
+ }),
4115
+ });
4116
+
4117
+ /**
4118
+ * Schema for validating AdapterContext.
4119
+ * Must always contain both adapter and chain explicitly.
4120
+ */
4121
+ const adapterContextSchema = z.object({
4122
+ adapter: adapterSchema,
4123
+ chain: chainIdentifierSchema,
4124
+ });
4125
+ /**
4126
+ * Schema for validating BridgeDestinationWithAddress objects.
4127
+ * Contains an explicit recipientAddress along with adapter and chain.
4128
+ * The address format is validated based on the chain type (EVM or Solana).
4129
+ */
4130
+ const bridgeDestinationWithAddressSchema = adapterContextSchema
4131
+ .extend({
4132
+ recipientAddress: z.string().min(1, 'Recipient address is required'),
4133
+ })
4134
+ .superRefine((data, ctx) => {
4135
+ const chain = data.chain;
4136
+ if (chain === null) {
4137
+ return;
4138
+ }
4139
+ if (!isValidAddressForChain(data.recipientAddress, chain)) {
4140
+ const chainInfo = extractChainInfo(chain);
4141
+ ctx.addIssue({
4142
+ code: z.ZodIssueCode.custom,
4143
+ path: ['recipientAddress'],
4144
+ message: `Invalid address format for ${String(chainInfo.name)}. Expected ${chainInfo.expectedAddressFormat}, but received: ${data.recipientAddress}`,
4145
+ });
4146
+ }
4147
+ });
4148
+ /**
4149
+ * Schema for validating BridgeDestination union type.
4150
+ * Can be an AdapterContext or BridgeDestinationWithAddress.
4151
+ *
4152
+ * The order matters: we check the more specific schema (with recipientAddress) first.
4153
+ * This ensures that objects with an empty recipientAddress field are rejected rather
4154
+ * than silently treated as AdapterContext with the field ignored.
4155
+ */
4156
+ const bridgeDestinationSchema = z.union([
4157
+ bridgeDestinationWithAddressSchema,
4158
+ adapterContextSchema.strict(),
4159
+ ]);
4160
+ /**
4161
+ * Schema for validating bridge parameters with chain identifiers.
4162
+ * This extends the core provider's schema but adapts it for the bridge kit's
4163
+ * more flexible interface that accepts chain identifiers.
4164
+ *
4165
+ * The schema validates:
4166
+ * - From adapter context (must always include both adapter and chain)
4167
+ * - To bridge destination (AdapterContext or BridgeDestinationWithAddress)
4168
+ * - Amount is a non-empty numeric string \> 0
4169
+ * - Token is optional and defaults to 'USDC'
4170
+ * - Optional config parameters (transfer speed, max fee)
4171
+ *
4172
+ * @example
4173
+ * ```typescript
4174
+ * import { bridgeParamsWithChainIdentifierSchema } from '@circle-fin/bridge-kit'
4175
+ *
4176
+ * const params = {
4177
+ * from: {
4178
+ * adapter: sourceAdapter,
4179
+ * chain: 'Ethereum'
4180
+ * },
4181
+ * to: {
4182
+ * adapter: destAdapter,
4183
+ * chain: 'Base'
4184
+ * },
4185
+ * amount: '100.50',
4186
+ * token: 'USDC',
4187
+ * config: {
4188
+ * transferSpeed: 'FAST'
4189
+ * }
4190
+ * }
4191
+ *
4192
+ * const result = bridgeParamsWithChainIdentifierSchema.safeParse(params)
4193
+ * if (result.success) {
4194
+ * console.log('Parameters are valid')
4195
+ * } else {
4196
+ * console.error('Validation failed:', result.error)
4197
+ * }
4198
+ * ```
4199
+ */
4200
+ const bridgeParamsWithChainIdentifierSchema = z.object({
4201
+ from: adapterContextSchema,
4202
+ to: bridgeDestinationSchema,
4203
+ amount: z
4204
+ .string()
4205
+ .min(1, 'Required')
4206
+ .pipe(createDecimalStringValidator({
4207
+ allowZero: false,
4208
+ regexMessage: 'Amount must be a numeric string with optional decimal places (e.g., 10.5, 10,5, 1.000,50 or 1,000.50)',
4209
+ attributeName: 'amount',
4210
+ maxDecimals: 6,
4211
+ })(z.string())),
4212
+ token: z.literal('USDC').optional(),
4213
+ config: z
4214
+ .object({
4215
+ transferSpeed: z.nativeEnum(TransferSpeed).optional(),
4216
+ maxFee: z
4217
+ .string()
4218
+ .regex(/^\d+$/, 'Max fee must be a numeric string')
4219
+ .optional(),
4220
+ customFee: customFeeSchema.optional(),
4221
+ })
4222
+ .optional(),
4223
+ });
4224
+
4225
+ /**
4226
+ * Resolves a chain identifier to a chain definition.
4227
+ *
4228
+ * Both AdapterContext and BridgeDestinationWithAddress have the chain property
4229
+ * at the top level, so we can directly access it from either type.
4230
+ *
4231
+ * @param ctx - The bridge destination containing the chain identifier
4232
+ * @returns The resolved chain definition
4233
+ * @throws If the chain definition cannot be resolved
4234
+ *
4235
+ * @example
4236
+ * ```typescript
4237
+ * import { Blockchain } from '@core/chains'
4238
+ *
4239
+ * // AdapterContext
4240
+ * const chain1 = resolveChainDefinition({
4241
+ * adapter: mockAdapter,
4242
+ * chain: 'Ethereum'
4243
+ * })
4244
+ *
4245
+ * // BridgeDestinationWithAddress
4246
+ * const chain2 = resolveChainDefinition({
4247
+ * adapter: mockAdapter,
4248
+ * chain: 'Base',
4249
+ * recipientAddress: '0x123...'
4250
+ * })
4251
+ * ```
4252
+ */
4253
+ function resolveChainDefinition(ctx) {
4254
+ return resolveChainIdentifier(ctx.chain);
4255
+ }
4256
+ /**
4257
+ * Resolves an address from a bridge destination.
4258
+ *
4259
+ * It handles two cases:
4260
+ * - BridgeDestinationWithAddress - returns the explicit recipientAddress
4261
+ * - AdapterContext - calls getAddress() on the adapter
4262
+ *
4263
+ * @param ctx - The bridge destination to resolve the address from
4264
+ * @returns The resolved address string
4265
+ *
4266
+ * @example
4267
+ * ```typescript
4268
+ * // Explicit recipient address
4269
+ * const addr1 = await resolveAddress({
4270
+ * adapter: mockAdapter,
4271
+ * chain: 'Ethereum',
4272
+ * recipientAddress: '0x1234567890123456789012345678901234567890'
4273
+ * }) // Returns: '0x1234567890123456789012345678901234567890'
4274
+ *
4275
+ * // Derived from adapter
4276
+ * const addr2 = await resolveAddress({
4277
+ * adapter: mockAdapter,
4278
+ * chain: 'Ethereum'
4279
+ * }) // Returns adapter's address
4280
+ * ```
4281
+ */
4282
+ async function resolveAddress(ctx) {
4283
+ // Handle BridgeDestinationWithAddress (has explicit recipientAddress)
4284
+ if ('recipientAddress' in ctx) {
4285
+ return ctx.recipientAddress;
4286
+ }
4287
+ // Handle AdapterContext (derive from adapter)
4288
+ // Pass the chain to support OperationContext pattern
4289
+ const chain = resolveChainDefinition(ctx);
4290
+ return await ctx.adapter.getAddress(chain);
4291
+ }
4292
+ /**
4293
+ * Resolves the amount of a transfer by formatting it according to the token's decimal places.
4294
+ *
4295
+ * This function takes the raw amount from the transfer parameters and formats it
4296
+ * using the appropriate decimal places for the specified token. Currently supports
4297
+ * USDC (6 decimals) and falls back to the raw amount for other tokens.
4298
+ *
4299
+ * @param params - The bridge parameters containing the amount, token type, and from context
4300
+ * @returns The formatted amount string with proper decimal places
4301
+ *
4302
+ * @example
4303
+ * ```typescript
4304
+ * import { Adapter } from '@core/adapter'
4305
+ *
4306
+ * const params = {
4307
+ * amount: '1000000',
4308
+ * token: 'USDC',
4309
+ * from: { adapter: mockAdapter, chain: Ethereum },
4310
+ * to: { adapter: mockAdapter, chain: Base }
4311
+ * }
4312
+ * const formattedAmount = resolveAmount(params) // Returns '1000000000000'
4313
+ * ```
4314
+ */
4315
+ function resolveAmount(params) {
4316
+ if (params.token === 'USDC') {
4317
+ return parseUnits(params.amount, 6).toString();
4318
+ }
4319
+ return params.amount;
4320
+ }
4321
+ /**
4322
+ * Resolves and normalizes bridge configuration for the provider.
4323
+ *
4324
+ * This function takes the optional configuration from bridge parameters and returns
4325
+ * a normalized BridgeConfig with:
4326
+ * - Default transfer speed set to FAST if not provided
4327
+ * - Custom fee values formatted with proper decimal places (6 decimals for USDC)
4328
+ *
4329
+ * @param params - The bridge parameters containing optional configuration
4330
+ * @returns A normalized BridgeConfig with defaults applied and values formatted
4331
+ *
4332
+ * @example
4333
+ * ```typescript
4334
+ * import { TransferSpeed } from '@core/provider'
4335
+ *
4336
+ * const params = {
4337
+ * amount: '100',
4338
+ * token: 'USDC',
4339
+ * from: { adapter: mockAdapter, chain: Ethereum },
4340
+ * to: { adapter: mockAdapter, chain: Base },
4341
+ * config: {
4342
+ * customFee: { value: '0.5' }
4343
+ * }
4344
+ * }
4345
+ * const config = resolveConfig(params)
4346
+ * // Returns: {
4347
+ * // transferSpeed: TransferSpeed.FAST,
4348
+ * // customFee: { value: '500000' }
4349
+ * // }
4350
+ * ```
4351
+ */
4352
+ function resolveConfig(params) {
4353
+ return {
4354
+ ...params.config,
4355
+ transferSpeed: params.config?.transferSpeed ?? TransferSpeed.FAST,
4356
+ customFee: params.config?.customFee
4357
+ ? {
4358
+ ...params.config?.customFee,
4359
+ value: params.config?.customFee?.value
4360
+ ? parseUnits(params.config?.customFee?.value, 6).toString()
4361
+ : undefined,
4362
+ }
4363
+ : undefined,
4364
+ };
4365
+ }
4366
+ /**
4367
+ * Resolves and normalizes bridge parameters for the BridgeKit.
4368
+ *
4369
+ * This function takes bridge parameters with explicit adapter contexts and normalizes them
4370
+ * into a consistent format that can be used by the bridging providers.
4371
+ *
4372
+ * The function performs parallel resolution of:
4373
+ * - Source and destination addresses
4374
+ * - Source and destination chain definitions
4375
+ * - Amount formatting
4376
+ *
4377
+ * @param params - The bridge parameters containing source/destination contexts, amount, and token
4378
+ * @returns Promise resolving to normalized bridge parameters for provider consumption
4379
+ * @throws \{Error\} If parameters cannot be resolved (invalid chains, etc.)
4380
+ *
4381
+ * @example
4382
+ * ```typescript
4383
+ * import { BridgeKit } from '@bridge-kit'
4384
+ *
4385
+ * const params = {
4386
+ * from: { adapter: sourceAdapter, chain: 'Ethereum' },
4387
+ * to: { adapter: destAdapter, chain: 'Base' },
4388
+ * amount: '10.5',
4389
+ * token: 'USDC'
4390
+ * }
4391
+ *
4392
+ * const resolved = await resolveBridgeParams(params)
4393
+ * console.log('Normalized for provider:', resolved)
4394
+ * ```
4395
+ */
4396
+ async function resolveBridgeParams(params) {
4397
+ const fromChain = resolveChainDefinition(params.from);
4398
+ const toChain = resolveChainDefinition(params.to);
4399
+ const [fromAddress, toAddress] = await Promise.all([
4400
+ resolveAddress(params.from),
4401
+ resolveAddress(params.to),
4402
+ ]);
4403
+ const token = params.token ?? 'USDC';
4404
+ // Extract adapters - now always from explicit contexts
4405
+ const fromAdapter = params.from.adapter;
4406
+ const toAdapter = params.to.adapter;
4407
+ return {
4408
+ amount: resolveAmount({
4409
+ ...params,
4410
+ token,
4411
+ }),
4412
+ token,
4413
+ config: resolveConfig({
4414
+ ...params}),
4415
+ source: {
4416
+ adapter: fromAdapter,
4417
+ chain: fromChain,
4418
+ address: fromAddress,
4419
+ },
4420
+ destination: {
4421
+ adapter: toAdapter,
4422
+ chain: toChain,
4423
+ address: toAddress,
4424
+ },
4425
+ };
4426
+ }
4427
+
4428
+ /**
4429
+ * The default providers that will be used in addition to the providers provided
4430
+ * to the BridgeKit constructor.
4431
+ */
4432
+ const getDefaultProviders = () => [new CCTPV2BridgingProvider()];
4433
+
4434
+ /**
4435
+ * Route cross-chain USDC bridging through Circle's Cross-Chain Transfer Protocol v2 (CCTPv2).
4436
+ *
4437
+ * This method orchestrates the entire cross-chain bridging process including:
4438
+ * 1. Parameter validation and route resolution
4439
+ * 2. Provider selection and configuration
4440
+ * 3. Transaction execution on both source and destination chains
4441
+ * 4. Event emission for monitoring and debugging
4442
+ *
4443
+ * The process is atomic - if any step fails, the method will throw an error
4444
+ * with detailed information about the failure point and any completed steps.
4445
+ *
4446
+ * @param params - The bridge parameters containing source, destination, amount, and token
4447
+ * @returns Promise resolving to the bridge result with transaction details and steps
4448
+ * @throws {ValidationError} If the parameters are invalid
4449
+ * @throws {BridgeError} If the bridging process fails
4450
+ * @throws {UnsupportedRouteError} If the route is not supported
4451
+ *
4452
+ * @example
4453
+ * ```typescript
4454
+ * import { BridgeKit } from '@circle-fin/bridge-kit'
4455
+ * import { createAdapterFromPrivateKey } from '@circle-fin/adapter-viem-v2'
4456
+ *
4457
+ * // Create kit with default CCTPv2 provider
4458
+ * const kit = new BridgeKit()
4459
+ * const adapter = createAdapterFromPrivateKey({ privateKey: '0x...' })
4460
+ *
4461
+ * // Execute cross-chain transfer
4462
+ * const result = await kit.bridge({
4463
+ * from: { adapter, chain: 'Ethereum' },
4464
+ * to: { adapter, chain: 'Base' },
4465
+ * amount: '10.50'
4466
+ * })
4467
+ *
4468
+ * // Monitor bridge events
4469
+ * kit.on('approve', (payload) => {
4470
+ * console.log('Approval complete:', payload.values.txHash)
4471
+ * })
4472
+ * ```
4473
+ */
4474
+ class BridgeKit {
4475
+ /**
4476
+ * The providers used for executing transfers.
4477
+ */
4478
+ providers;
4479
+ /**
4480
+ * The action dispatcher for the kit.
4481
+ */
4482
+ actionDispatcher;
4483
+ /**
4484
+ * A custom fee policy for the kit.
4485
+ */
4486
+ customFeePolicy;
4487
+ /**
4488
+ * Create a new BridgeKit instance.
4489
+ *
4490
+ * @param config - The configuration containing the CCTPv2 provider
4491
+ *
4492
+ * @example
4493
+ * ```typescript
4494
+ * import { BridgeKit } from '@circle-fin/bridge-kit'
4495
+ *
4496
+ * const kit = new BridgeKit()
4497
+ * ```
4498
+ */
4499
+ constructor(config = {}) {
4500
+ // Handle provider configuration
4501
+ const defaultProviders = getDefaultProviders();
4502
+ this.providers = [...defaultProviders, ...(config.providers ?? [])];
4503
+ this.actionDispatcher = new Actionable();
4504
+ for (const provider of this.providers) {
4505
+ provider.registerDispatcher(this.actionDispatcher);
4506
+ }
4507
+ }
4508
+ // implementation just forwards to the bus
4509
+ on(actionOrWildCard, handler) {
4510
+ this.actionDispatcher.on(actionOrWildCard, handler);
4511
+ }
4512
+ // implementation just forwards to the bus
4513
+ off(actionOrWildCard, handler) {
4514
+ this.actionDispatcher.off(actionOrWildCard, handler);
4515
+ }
4516
+ /**
4517
+ * Execute a cross-chain USDC transfer using CCTPv2.
4518
+ *
4519
+ * Handle the complete CCTPv2 transfer flow, including parameter validation,
4520
+ * chain resolution, and transfer execution. Provide comprehensive validation of
4521
+ * all parameters before initiating the transfer.
4522
+ *
4523
+ * Perform validation of:
4524
+ * - Source and destination wallet contexts
4525
+ * - Chain identifiers (string, enum, or chain definition)
4526
+ * - Amount format and token type
4527
+ * - CCTPv2 support for the chain pair
4528
+ * - Transfer configuration options
4529
+ *
4530
+ * @param params - The transfer parameters containing source, destination, amount, and token
4531
+ * @returns Promise resolving to the transfer result with transaction details and steps
4532
+ * @throws {ValidationError} When any parameter validation fails.
4533
+ * @throws {Error} When CCTPv2 does not support the specified route.
4534
+ *
4535
+ * @example
4536
+ * ```typescript
4537
+ * import { BridgeKit } from '@circle-fin/bridge-kit'
4538
+ * import { createAdapterFromPrivateKey } from '@circle-fin/adapter-viem-v2'
4539
+ *
4540
+ * const kit = new BridgeKit()
4541
+ *
4542
+ * // Create a single adapter that can work across chains
4543
+ * const adapter = createAdapterFromPrivateKey({
4544
+ * privateKey: process.env.PRIVATE_KEY,
4545
+ * })
4546
+ *
4547
+ * const result = await kit.bridge({
4548
+ * from: {
4549
+ * adapter,
4550
+ * chain: 'Ethereum'
4551
+ * },
4552
+ * to: {
4553
+ * adapter,
4554
+ * chain: 'Base'
4555
+ * },
4556
+ * amount: '100.50'
4557
+ * })
4558
+ *
4559
+ * // Handle result
4560
+ * if (result.state === 'success') {
4561
+ * console.log('Bridge completed!')
4562
+ * result.steps.forEach(step => {
4563
+ * console.log(`${step.name}: ${step.explorerUrl}`)
4564
+ * })
4565
+ * } else {
4566
+ * console.error('Bridge failed:', result.steps)
4567
+ * }
4568
+ * ```
4569
+ */
4570
+ async bridge(params) {
4571
+ // First validate the parameters
4572
+ assertBridgeParams(params, bridgeParamsWithChainIdentifierSchema);
4573
+ // Then resolve chain definitions
4574
+ const resolvedParams = await resolveBridgeParams(params);
4575
+ // Validate network compatibility
4576
+ this.validateNetworkCompatibility(resolvedParams);
4577
+ // Merge the custom fee config into the resolved params
4578
+ const finalResolvedParams = await this.mergeCustomFeeConfig(resolvedParams);
4579
+ // Find a provider that supports this route
4580
+ const provider = this.findProviderForRoute(finalResolvedParams);
4581
+ // Execute the transfer using the provider
4582
+ return provider.bridge(finalResolvedParams);
4583
+ }
4584
+ /**
4585
+ * Retry a failed or incomplete cross-chain USDC bridge operation.
4586
+ *
4587
+ * Provide a high-level interface for resuming bridge operations that have failed
4588
+ * or become stuck during execution. Automatically identify the provider that was
4589
+ * used for the original transfer and delegate the retry logic to that provider's
4590
+ * implementation.
4591
+ *
4592
+ * Use this functionality to handle:
4593
+ * - Network timeouts or temporary connectivity issues
4594
+ * - Gas estimation failures that can be resolved with updated parameters
4595
+ * - Pending transactions that need to be resubmitted
4596
+ * - Failed steps in multi-step bridge flows
4597
+ *
4598
+ * @param result - The bridge result from a previous failed or incomplete operation.
4599
+ * Must contain the provider name and step execution history.
4600
+ * @param context - The retry context containing fresh adapter instances for both
4601
+ * source and destination chains. These adapters should be properly
4602
+ * configured with current network connections and signing capabilities.
4603
+ * @returns A promise that resolves to the updated bridge result after retry execution.
4604
+ * The result will contain the complete step history including both original
4605
+ * and retry attempts.
4606
+ *
4607
+ * @throws {Error} When the original provider specified in the result is not found
4608
+ * in the current kit configuration.
4609
+ * @throws {Error} When the underlying provider's retry operation fails due to
4610
+ * non-recoverable errors or invalid state.
4611
+ *
4612
+ * @example
4613
+ * ```typescript
4614
+ * import { BridgeKit } from '@circle-fin/bridge-kit'
4615
+ * import { createAdapterFromPrivateKey } from '@circle-fin/adapter-viem-v2'
4616
+ *
4617
+ * const kit = new BridgeKit()
4618
+ *
4619
+ * // Assume we have a failed bridge result from a previous operation
4620
+ * const failedResult: BridgeResult = {
4621
+ * state: 'error',
4622
+ * provider: 'CCTPV2BridgingProvider',
4623
+ * steps: [
4624
+ * { name: 'approve', state: 'success', txHash: '0x123...' },
4625
+ * { name: 'burn', state: 'error', errorMessage: 'Gas limit exceeded' }
4626
+ * ],
4627
+ * // ... other properties
4628
+ * }
4629
+ *
4630
+ *
4631
+ * try {
4632
+ * const retryResult = await kit.retry(failedResult, {
4633
+ * from: sourceAdapter,
4634
+ * to: destAdapter
4635
+ * })
4636
+ *
4637
+ * console.log('Retry completed successfully:', retryResult.state)
4638
+ * console.log('Total steps executed:', retryResult.steps.length)
4639
+ * } catch (error) {
4640
+ * console.error('Retry failed:', error.message)
4641
+ * // Handle retry failure (may require manual intervention)
4642
+ * }
4643
+ * ```
4644
+ */
4645
+ async retry(result, context) {
4646
+ const provider = this.providers.find((p) => p.name === result.provider);
4647
+ if (!provider) {
4648
+ throw new Error(`Provider ${result.provider} not found`);
4649
+ }
4650
+ return provider.retry(result, context);
4651
+ }
4652
+ /**
4653
+ * Estimate the cost and fees for a cross-chain USDC bridge operation.
4654
+ *
4655
+ * This method calculates the expected gas fees and protocol costs for bridging
4656
+ * without actually executing the transaction. It performs the same validation
4657
+ * as the bridge method but stops before execution.
4658
+ * @param params - The bridge parameters for cost estimation
4659
+ * @returns Promise resolving to detailed cost breakdown including gas estimates
4660
+ * @throws {ValidationError} When the parameters are invalid.
4661
+ * @throws {UnsupportedRouteError} When the route is not supported.
4662
+ *
4663
+ * @example
4664
+ * ```typescript
4665
+ * const estimate = await kit.estimate({
4666
+ * from: { adapter: adapter, chain: 'Ethereum' },
4667
+ * to: { adapter: adapter, chain: 'Base' },
4668
+ * amount: '10.50',
4669
+ * token: 'USDC'
4670
+ * })
4671
+ * console.log('Estimated cost:', estimate.totalCost)
4672
+ * ```
4673
+ */
4674
+ async estimate(params) {
4675
+ // First validate the parameters
4676
+ assertBridgeParams(params, bridgeParamsWithChainIdentifierSchema);
4677
+ // Then resolve chain definitions
4678
+ const resolvedParams = await resolveBridgeParams(params);
4679
+ // Validate network compatibility
4680
+ this.validateNetworkCompatibility(resolvedParams);
4681
+ // Merge the custom fee config into the resolved params
4682
+ const finalResolvedParams = await this.mergeCustomFeeConfig(resolvedParams);
4683
+ // Find a provider that supports this route
4684
+ const provider = this.findProviderForRoute(finalResolvedParams);
4685
+ // Estimate the transfer using the provider
4686
+ return provider.estimate(finalResolvedParams);
4687
+ }
4688
+ /**
4689
+ * Get all chains supported by any provider in the kit.
4690
+ *
4691
+ * Aggregate and deduplicate the supported chains from all registered providers.
4692
+ * This provides a comprehensive list of chains that can be used as either source
4693
+ * or destination for transfers through this kit instance.
4694
+ *
4695
+ * The method automatically deduplicates chains based on their chain identifier,
4696
+ * ensuring each chain appears only once in the result regardless of how many
4697
+ * providers support it.
4698
+ *
4699
+ * @returns Array of unique chain definitions supported by the registered providers
4700
+ *
4701
+ * @example
4702
+ * ```typescript
4703
+ * import { BridgeKit } from '@circle-fin/bridge-kit'
4704
+ *
4705
+ * const kit = new BridgeKit()
4706
+ * const chains = kit.getSupportedChains()
4707
+ *
4708
+ * console.log('Supported chains:')
4709
+ * chains.forEach(chain => {
4710
+ * console.log(`- ${chain.name} (${chain.type})`)
4711
+ * })
4712
+ * ```
4713
+ */
4714
+ getSupportedChains() {
4715
+ const supportedChains = this.providers.flatMap((p) => p.supportedChains);
4716
+ // Deduplicate chains by using chain identifiers as object keys
4717
+ // Later duplicates will override earlier ones, keeping only the last occurrence
4718
+ return Object.values(Object.fromEntries(supportedChains.map((chain) => [chain.chain, chain])));
4719
+ }
4720
+ /**
4721
+ * Validate that source and destination chains are on the same network type.
4722
+ *
4723
+ * This method ensures that both chains are either testnet or mainnet, preventing
4724
+ * cross-network transfers which are not supported by the bridging protocols.
4725
+ *
4726
+ * @param resolvedParams - The resolved bridge parameters containing source and destination chains
4727
+ * @throws {NetworkMismatchError} If source and destination chains are on different network types
4728
+ */
4729
+ validateNetworkCompatibility(resolvedParams) {
4730
+ if (resolvedParams.source.chain.isTestnet !==
4731
+ resolvedParams.destination.chain.isTestnet) {
4732
+ throw createNetworkMismatchError(resolvedParams.source.chain, resolvedParams.destination.chain);
4733
+ }
4734
+ }
4735
+ /**
4736
+ * Find a provider that supports the given transfer route.
4737
+ *
4738
+ * This method centralizes the provider selection logic to ensure consistency
4739
+ * between transfer and estimate operations. It resolves the source and destination
4740
+ * chains from the provided adapters and finds the first provider that supports
4741
+ * the route for the specified token.
4742
+ *
4743
+ * @param params - The transfer parameters containing source, destination, and token
4744
+ * @returns Promise resolving to the provider that supports this route
4745
+ * @throws Will throw an error if no provider supports the route
4746
+ */
4747
+ findProviderForRoute(params) {
4748
+ const provider = this.providers.find((p) => p.supportsRoute(params.source.chain, params.destination.chain, params.token));
4749
+ if (!provider) {
4750
+ throw createUnsupportedRouteError(params.source.chain.name, params.destination.chain.name);
4751
+ }
4752
+ return provider;
4753
+ }
4754
+ /**
4755
+ * Merge custom fee configuration into provider parameters.
4756
+ *
4757
+ * Prioritizes any custom fee configuration already present on the
4758
+ * provider-resolved params and uses the kit-level custom fee configuration
4759
+ * as a fallback only when a value is missing. If neither the provider params
4760
+ * nor the kit configuration can supply a value, no custom fee configuration
4761
+ * is added to the provider params.
4762
+ *
4763
+ * @param originalParams - The original bridge parameters received by the kit.
4764
+ * @param providerParams - The provider-resolved bridge parameters that may be enriched.
4765
+ * @returns The same `providerParams` reference with custom fee configuration merged when applicable.
4766
+ *
4767
+ * @remarks
4768
+ * - Existing values on `providerParams.config.customFee` are preserved.
4769
+ * - Kit-level functions are invoked lazily and only for missing values.
4770
+ * - If both sources provide no values, `customFee` is omitted entirely.
4771
+ */
4772
+ async mergeCustomFeeConfig(providerParams) {
4773
+ // Prefer any custom fee already resolved by the provider
4774
+ const existingFee = providerParams.config?.customFee?.value;
4775
+ const existingFeeRecipient = providerParams.config?.customFee?.recipientAddress;
4776
+ // Fill missing values using kit-level configuration (if available)
4777
+ const fee = existingFee ?? (await this.customFeePolicy?.calculateFee(providerParams));
4778
+ const feeRecipient = existingFeeRecipient ??
4779
+ (await this.customFeePolicy?.resolveFeeRecipientAddress(providerParams.source.chain, providerParams));
4780
+ // Only attach customFee if at least one value is defined
4781
+ if (fee !== undefined || feeRecipient !== undefined) {
4782
+ providerParams.config = {
4783
+ ...providerParams.config,
4784
+ customFee: {
4785
+ value: fee,
4786
+ recipientAddress: feeRecipient,
4787
+ },
4788
+ };
4789
+ }
4790
+ return providerParams;
4791
+ }
4792
+ /**
4793
+ * Sets the custom fee policy for the kit.
4794
+ *
4795
+ * Ensures the fee is represented in the smallest unit of the token.
4796
+ * - If the token is USDC, the fee is converted to base units (6 decimals).
4797
+ * - If the token is not USDC, the fee is returned as is.
4798
+ *
4799
+ * This allows developers to specify the kit-level fee in base units (e.g., USDC: 1, ETH: 0.000001),
4800
+ * and the kit will handle conversion to the smallest unit as needed.
4801
+ *
4802
+ * @param customFeePolicy - The custom fee policy to set.
4803
+ * @throws {ValidationError} If the custom fee policy is invalid or missing required functions
4804
+ *
4805
+ * @example
4806
+ * ```typescript
4807
+ * import { BridgeKit } from '@circle-fin/bridge-kit'
4808
+ * import { Blockchain } from '@core/chains'
4809
+ *
4810
+ * const kit = new BridgeKit()
4811
+ *
4812
+ * kit.setCustomFeePolicy({
4813
+ * calculateFee: (params) => {
4814
+ * // Return decimal string - kit converts to smallest units automatically
4815
+ * // '0.1' becomes '100000' (0.1 USDC with 6 decimals)
4816
+ * return params.source.chain.chain === Blockchain.Ethereum_Sepolia
4817
+ * ? '0.1' // 0.1 USDC
4818
+ * : '0.2'; // 0.2 USDC
4819
+ * },
4820
+ * resolveFeeRecipientAddress: (feePayoutChain, params) => {
4821
+ * // Return valid address for the source chain
4822
+ * return params.source.chain.chain === Blockchain.Ethereum_Sepolia
4823
+ * ? '0x23f9a5BEA7B92a0638520607407BC7f0310aEeD4'
4824
+ * : '0x1E1A18B7bD95bcFcFb4d6E245D289C1e95547b35';
4825
+ * },
4826
+ * });
4827
+ * ```
4828
+ */
4829
+ setCustomFeePolicy(customFeePolicy) {
4830
+ assertCustomFeePolicy(customFeePolicy);
4831
+ const { calculateFee, resolveFeeRecipientAddress } = customFeePolicy;
4832
+ // Format the calculateFee function to convert the fee to the smallest unit of the token
4833
+ const formattedCalculateFee = async (params) => {
4834
+ const fee = await calculateFee(params);
4835
+ const token = params.token ?? 'USDC';
4836
+ return token === 'USDC' ? parseUnits(fee, 6).toString() : fee;
4837
+ };
4838
+ // Return a new custom fee policy with the formatted calculateFee function
4839
+ this.customFeePolicy = {
4840
+ calculateFee: formattedCalculateFee,
4841
+ resolveFeeRecipientAddress,
4842
+ };
4843
+ }
4844
+ /**
4845
+ * Remove the custom fee policy for the kit.
4846
+ *
4847
+ * @example
4848
+ * ```typescript
4849
+ * kit.removeCustomFeePolicy()
4850
+ * ```
4851
+ */
4852
+ removeCustomFeePolicy() {
4853
+ this.customFeePolicy = undefined;
4854
+ }
4855
+ }
4856
+
4857
+ export { Blockchain, BridgeKit, KitError, bridgeParamsWithChainIdentifierSchema };
4858
+ //# sourceMappingURL=index.mjs.map