@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/LICENSE +190 -0
- package/QUICKSTART.md +1147 -0
- package/README.md +478 -0
- package/chains.cjs.js +2250 -0
- package/chains.cjs.js.map +1 -0
- package/chains.d.ts +1088 -0
- package/chains.mjs +2225 -0
- package/chains.mjs.map +1 -0
- package/index.cjs.js +4862 -0
- package/index.cjs.js.map +1 -0
- package/index.d.ts +4048 -0
- package/index.mjs +4858 -0
- package/index.mjs.map +1 -0
- package/package.json +46 -0
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
|