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