@circle-fin/provider-cctp-v2 1.0.2 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +32 -0
- package/index.cjs +160 -15
- package/index.d.ts +11 -3
- package/index.mjs +160 -15
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,37 @@
|
|
|
1
1
|
# @circle-fin/provider-cctp-v2
|
|
2
2
|
|
|
3
|
+
## 1.0.4
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Fixed bug where tokens could be burned when bridging to unsupported chains, and improved error messages to clearly show which chains are supported.
|
|
8
|
+
|
|
9
|
+
**What's Fixed:**
|
|
10
|
+
- **Prevents fund loss**: Bridge operations now fail immediately if your adapter doesn't support the source or destination chain, **before** any tokens are approved or burned. Previously, tokens could be burned on the source chain before discovering the destination chain was unsupported, requiring manual recovery.
|
|
11
|
+
- **Better error messages**: When you attempt to use an unsupported chain, the error now clearly lists all chains your adapter supports, making it easy to pick an alternative:
|
|
12
|
+
```
|
|
13
|
+
Invalid chain 'Linea Sepolia': Not supported by this adapter.
|
|
14
|
+
It supports 17 chains: Arbitrum, Base, Ethereum, Polygon, Solana, ...
|
|
15
|
+
```
|
|
16
|
+
- **Correct error codes**: Chain validation errors now use the correct `INVALID_CHAIN` error code instead of `UNSUPPORTED_ROUTE`, making it easier to handle errors programmatically.
|
|
17
|
+
|
|
18
|
+
- Improved error handling with more informative and consistent error messages.
|
|
19
|
+
|
|
20
|
+
Errors now include:
|
|
21
|
+
- Specific error codes for programmatic handling
|
|
22
|
+
- Error type categorization (BALANCE, ONCHAIN, RPC, NETWORK)
|
|
23
|
+
- Recoverability information (FATAL vs RETRYABLE)
|
|
24
|
+
- Clearer error messages with chain context
|
|
25
|
+
- Original error details preserved for debugging
|
|
26
|
+
|
|
27
|
+
- Fixed an issue where bridging USDC to Solana would fail when specifying a recipient address different from the transaction signing wallet. The adapter now correctly creates the Associated Token Account (ATA) for the specified recipient address and delivers USDC to that address.
|
|
28
|
+
|
|
29
|
+
## 1.0.3
|
|
30
|
+
|
|
31
|
+
### Patch Changes
|
|
32
|
+
|
|
33
|
+
- Improves the Solana CCTP v2 custom burn flow to ensure developer fee payouts never fail due to a missing associated token account (ATA). The instruction builder now checks for the existence of the developer fee recipient's ATA and, if absent, emits an idempotent creation instruction.
|
|
34
|
+
|
|
3
35
|
## 1.0.2
|
|
4
36
|
|
|
5
37
|
### Patch Changes
|
package/index.cjs
CHANGED
|
@@ -4191,9 +4191,71 @@ const RECOVERABILITY_VALUES = [
|
|
|
4191
4191
|
'RESUMABLE',
|
|
4192
4192
|
'FATAL',
|
|
4193
4193
|
];
|
|
4194
|
+
/**
|
|
4195
|
+
* Error type constants for categorizing errors by origin.
|
|
4196
|
+
*
|
|
4197
|
+
* This const object provides a reference for error types, enabling
|
|
4198
|
+
* IDE autocomplete and preventing typos when creating custom errors.
|
|
4199
|
+
*
|
|
4200
|
+
* @remarks
|
|
4201
|
+
* While internal error definitions use string literals with type annotations
|
|
4202
|
+
* for strict type safety, this constant is useful for developers creating
|
|
4203
|
+
* custom error instances or checking error types programmatically.
|
|
4204
|
+
*
|
|
4205
|
+
* @example
|
|
4206
|
+
* ```typescript
|
|
4207
|
+
* import { ERROR_TYPES, KitError } from '@core/errors'
|
|
4208
|
+
*
|
|
4209
|
+
* // Use for type checking
|
|
4210
|
+
* if (error.type === ERROR_TYPES.BALANCE) {
|
|
4211
|
+
* console.log('This is a balance error')
|
|
4212
|
+
* }
|
|
4213
|
+
* ```
|
|
4214
|
+
*
|
|
4215
|
+
* @example
|
|
4216
|
+
* ```typescript
|
|
4217
|
+
* // Use as reference when creating custom errors
|
|
4218
|
+
* const error = new KitError({
|
|
4219
|
+
* code: 9999,
|
|
4220
|
+
* name: 'CUSTOM_ERROR',
|
|
4221
|
+
* type: ERROR_TYPES.BALANCE, // IDE autocomplete works here
|
|
4222
|
+
* recoverability: 'FATAL',
|
|
4223
|
+
* message: 'Custom balance error'
|
|
4224
|
+
* })
|
|
4225
|
+
* ```
|
|
4226
|
+
*/
|
|
4227
|
+
const ERROR_TYPES = {
|
|
4228
|
+
/** User input validation and parameter checking */
|
|
4229
|
+
INPUT: 'INPUT',
|
|
4230
|
+
/** Insufficient token balances and amount validation */
|
|
4231
|
+
BALANCE: 'BALANCE',
|
|
4232
|
+
/** On-chain execution: reverts, gas issues, transaction failures */
|
|
4233
|
+
ONCHAIN: 'ONCHAIN',
|
|
4234
|
+
/** Blockchain RPC provider issues and endpoint problems */
|
|
4235
|
+
RPC: 'RPC',
|
|
4236
|
+
/** Internet connectivity, DNS resolution, connection issues */
|
|
4237
|
+
NETWORK: 'NETWORK',
|
|
4238
|
+
};
|
|
4239
|
+
/**
|
|
4240
|
+
* Array of valid error type values for validation.
|
|
4241
|
+
* Derived from ERROR_TYPES const object.
|
|
4242
|
+
*/
|
|
4243
|
+
const ERROR_TYPE_VALUES = Object.values(ERROR_TYPES);
|
|
4194
4244
|
|
|
4195
|
-
// Create
|
|
4245
|
+
// Create mutable arrays for Zod enum validation
|
|
4196
4246
|
const RECOVERABILITY_ARRAY = [...RECOVERABILITY_VALUES];
|
|
4247
|
+
const ERROR_TYPE_ARRAY = [...ERROR_TYPE_VALUES];
|
|
4248
|
+
/**
|
|
4249
|
+
* Error code ranges for validation.
|
|
4250
|
+
* Single source of truth for valid error code ranges.
|
|
4251
|
+
*/
|
|
4252
|
+
const ERROR_CODE_RANGES = [
|
|
4253
|
+
{ min: 1000, max: 1999, type: 'INPUT' },
|
|
4254
|
+
{ min: 3000, max: 3999, type: 'NETWORK' },
|
|
4255
|
+
{ min: 4000, max: 4999, type: 'RPC' },
|
|
4256
|
+
{ min: 5000, max: 5999, type: 'ONCHAIN' },
|
|
4257
|
+
{ min: 9000, max: 9999, type: 'BALANCE' },
|
|
4258
|
+
];
|
|
4197
4259
|
/**
|
|
4198
4260
|
* Zod schema for validating ErrorDetails objects.
|
|
4199
4261
|
*
|
|
@@ -4206,7 +4268,8 @@ const RECOVERABILITY_ARRAY = [...RECOVERABILITY_VALUES];
|
|
|
4206
4268
|
*
|
|
4207
4269
|
* const result = errorDetailsSchema.safeParse({
|
|
4208
4270
|
* code: 1001,
|
|
4209
|
-
* name: '
|
|
4271
|
+
* name: 'INPUT_NETWORK_MISMATCH',
|
|
4272
|
+
* type: 'INPUT',
|
|
4210
4273
|
* recoverability: 'FATAL',
|
|
4211
4274
|
* message: 'Source and destination networks must be different'
|
|
4212
4275
|
* })
|
|
@@ -4215,30 +4278,56 @@ const RECOVERABILITY_ARRAY = [...RECOVERABILITY_VALUES];
|
|
|
4215
4278
|
* console.error('Validation failed:', result.error.issues)
|
|
4216
4279
|
* }
|
|
4217
4280
|
* ```
|
|
4281
|
+
*
|
|
4282
|
+
* @example
|
|
4283
|
+
* ```typescript
|
|
4284
|
+
* // Runtime error
|
|
4285
|
+
* const result = errorDetailsSchema.safeParse({
|
|
4286
|
+
* code: 9001,
|
|
4287
|
+
* name: 'BALANCE_INSUFFICIENT_TOKEN',
|
|
4288
|
+
* type: 'BALANCE',
|
|
4289
|
+
* recoverability: 'FATAL',
|
|
4290
|
+
* message: 'Insufficient USDC balance'
|
|
4291
|
+
* })
|
|
4292
|
+
* ```
|
|
4218
4293
|
*/
|
|
4219
4294
|
const errorDetailsSchema = zod.z.object({
|
|
4220
|
-
/**
|
|
4295
|
+
/**
|
|
4296
|
+
* Numeric identifier following standardized ranges:
|
|
4297
|
+
* - 1000-1999: INPUT errors - Parameter validation
|
|
4298
|
+
* - 3000-3999: NETWORK errors - Connectivity issues
|
|
4299
|
+
* - 4000-4999: RPC errors - Provider issues, gas estimation
|
|
4300
|
+
* - 5000-5999: ONCHAIN errors - Transaction/simulation failures
|
|
4301
|
+
* - 9000-9999: BALANCE errors - Insufficient funds
|
|
4302
|
+
*/
|
|
4221
4303
|
code: zod.z
|
|
4222
4304
|
.number()
|
|
4223
4305
|
.int('Error code must be an integer')
|
|
4224
|
-
.
|
|
4225
|
-
|
|
4226
|
-
|
|
4306
|
+
.refine((code) => ERROR_CODE_RANGES.some((range) => code >= range.min && code <= range.max), {
|
|
4307
|
+
message: 'Error code must be in valid ranges: 1000-1999 (INPUT), 3000-3999 (NETWORK), 4000-4999 (RPC), 5000-5999 (ONCHAIN), 9000-9999 (BALANCE)',
|
|
4308
|
+
}),
|
|
4309
|
+
/** Human-readable ID (e.g., "INPUT_NETWORK_MISMATCH", "BALANCE_INSUFFICIENT_TOKEN") */
|
|
4227
4310
|
name: zod.z
|
|
4228
4311
|
.string()
|
|
4229
4312
|
.min(1, 'Error name must be a non-empty string')
|
|
4230
4313
|
.regex(/^[A-Z_][A-Z0-9_]*$/, 'Error name must match pattern: ^[A-Z_][A-Z0-9_]*$'),
|
|
4314
|
+
/** Error category indicating where the error originated */
|
|
4315
|
+
type: zod.z.enum(ERROR_TYPE_ARRAY, {
|
|
4316
|
+
errorMap: () => ({
|
|
4317
|
+
message: 'Error type must be one of: INPUT, BALANCE, ONCHAIN, RPC, NETWORK',
|
|
4318
|
+
}),
|
|
4319
|
+
}),
|
|
4231
4320
|
/** Error handling strategy */
|
|
4232
4321
|
recoverability: zod.z.enum(RECOVERABILITY_ARRAY, {
|
|
4233
4322
|
errorMap: () => ({
|
|
4234
4323
|
message: 'Recoverability must be one of: RETRYABLE, RESUMABLE, FATAL',
|
|
4235
4324
|
}),
|
|
4236
4325
|
}),
|
|
4237
|
-
/** User-friendly explanation with
|
|
4326
|
+
/** User-friendly explanation with context */
|
|
4238
4327
|
message: zod.z
|
|
4239
4328
|
.string()
|
|
4240
4329
|
.min(1, 'Error message must be a non-empty string')
|
|
4241
|
-
.max(
|
|
4330
|
+
.max(1000, 'Error message must be 1000 characters or less'),
|
|
4242
4331
|
/** Raw error details, context, or the original error that caused this one. */
|
|
4243
4332
|
cause: zod.z
|
|
4244
4333
|
.object({
|
|
@@ -4253,7 +4342,7 @@ const errorDetailsSchema = zod.z.object({
|
|
|
4253
4342
|
*
|
|
4254
4343
|
* @param details - The object to validate
|
|
4255
4344
|
* @returns The validated ErrorDetails object
|
|
4256
|
-
* @throws
|
|
4345
|
+
* @throws TypeError When validation fails
|
|
4257
4346
|
*
|
|
4258
4347
|
* @example
|
|
4259
4348
|
* ```typescript
|
|
@@ -4330,6 +4419,8 @@ class KitError extends Error {
|
|
|
4330
4419
|
code;
|
|
4331
4420
|
/** Human-readable ID (e.g., "NETWORK_MISMATCH") */
|
|
4332
4421
|
name;
|
|
4422
|
+
/** Error category indicating where the error originated */
|
|
4423
|
+
type;
|
|
4333
4424
|
/** Error handling strategy */
|
|
4334
4425
|
recoverability;
|
|
4335
4426
|
/** Raw error details, context, or the original error that caused this one. */
|
|
@@ -4358,6 +4449,12 @@ class KitError extends Error {
|
|
|
4358
4449
|
enumerable: true,
|
|
4359
4450
|
configurable: false,
|
|
4360
4451
|
},
|
|
4452
|
+
type: {
|
|
4453
|
+
value: validatedDetails.type,
|
|
4454
|
+
writable: false,
|
|
4455
|
+
enumerable: true,
|
|
4456
|
+
configurable: false,
|
|
4457
|
+
},
|
|
4361
4458
|
recoverability: {
|
|
4362
4459
|
value: validatedDetails.recoverability,
|
|
4363
4460
|
writable: false,
|
|
@@ -4377,14 +4474,19 @@ class KitError extends Error {
|
|
|
4377
4474
|
}
|
|
4378
4475
|
|
|
4379
4476
|
/**
|
|
4380
|
-
*
|
|
4381
|
-
*
|
|
4477
|
+
* Standardized error code ranges for consistent categorization:
|
|
4478
|
+
*
|
|
4479
|
+
* - 1000-1999: INPUT errors - Parameter validation, input format errors
|
|
4480
|
+
* - 3000-3999: NETWORK errors - Internet connectivity, DNS, connection issues
|
|
4481
|
+
* - 4000-4999: RPC errors - Blockchain provider issues, gas estimation, nonce errors
|
|
4482
|
+
* - 5000-5999: ONCHAIN errors - Transaction/simulation failures, gas exhaustion, reverts
|
|
4483
|
+
* - 9000-9999: BALANCE errors - Insufficient funds, token balance, allowance
|
|
4382
4484
|
*/
|
|
4383
4485
|
/**
|
|
4384
4486
|
* Standardized error definitions for INPUT type errors.
|
|
4385
4487
|
*
|
|
4386
|
-
* Each entry combines the numeric error code
|
|
4387
|
-
*
|
|
4488
|
+
* Each entry combines the numeric error code, string name, and type
|
|
4489
|
+
* to ensure consistency when creating error instances.
|
|
4388
4490
|
*
|
|
4389
4491
|
* Error codes follow a hierarchical numbering scheme where the first digit
|
|
4390
4492
|
* indicates the error category (1 = INPUT) and subsequent digits provide
|
|
@@ -4401,9 +4503,10 @@ class KitError extends Error {
|
|
|
4401
4503
|
* message: 'Source and destination networks must be different'
|
|
4402
4504
|
* })
|
|
4403
4505
|
*
|
|
4404
|
-
* // Access code and
|
|
4506
|
+
* // Access code, name, and type individually if needed
|
|
4405
4507
|
* console.log(InputError.NETWORK_MISMATCH.code) // 1001
|
|
4406
4508
|
* console.log(InputError.NETWORK_MISMATCH.name) // 'INPUT_NETWORK_MISMATCH'
|
|
4509
|
+
* console.log(InputError.NETWORK_MISMATCH.type) // 'INPUT'
|
|
4407
4510
|
* ```
|
|
4408
4511
|
*/
|
|
4409
4512
|
const InputError = {
|
|
@@ -4411,16 +4514,19 @@ const InputError = {
|
|
|
4411
4514
|
NETWORK_MISMATCH: {
|
|
4412
4515
|
code: 1001,
|
|
4413
4516
|
name: 'INPUT_NETWORK_MISMATCH',
|
|
4517
|
+
type: 'INPUT',
|
|
4414
4518
|
},
|
|
4415
4519
|
/** Unsupported or invalid bridge route configuration */
|
|
4416
4520
|
UNSUPPORTED_ROUTE: {
|
|
4417
4521
|
code: 1003,
|
|
4418
4522
|
name: 'INPUT_UNSUPPORTED_ROUTE',
|
|
4523
|
+
type: 'INPUT',
|
|
4419
4524
|
},
|
|
4420
4525
|
/** General validation failure for complex validation rules */
|
|
4421
4526
|
VALIDATION_FAILED: {
|
|
4422
4527
|
code: 1098,
|
|
4423
4528
|
name: 'INPUT_VALIDATION_FAILED',
|
|
4529
|
+
type: 'INPUT',
|
|
4424
4530
|
},
|
|
4425
4531
|
};
|
|
4426
4532
|
|
|
@@ -4535,6 +4641,36 @@ function createValidationFailedError(field, value, reason) {
|
|
|
4535
4641
|
return new KitError(errorDetails);
|
|
4536
4642
|
}
|
|
4537
4643
|
|
|
4644
|
+
/**
|
|
4645
|
+
* Type guard to check if an error is a KitError instance.
|
|
4646
|
+
*
|
|
4647
|
+
* This guard enables TypeScript to narrow the type from `unknown` to
|
|
4648
|
+
* `KitError`, providing access to structured error properties like
|
|
4649
|
+
* code, name, and recoverability.
|
|
4650
|
+
*
|
|
4651
|
+
* @param error - Unknown error to check
|
|
4652
|
+
* @returns True if error is KitError with proper type narrowing
|
|
4653
|
+
*
|
|
4654
|
+
* @example
|
|
4655
|
+
* ```typescript
|
|
4656
|
+
* import { isKitError } from '@core/errors'
|
|
4657
|
+
*
|
|
4658
|
+
* try {
|
|
4659
|
+
* await kit.bridge(params)
|
|
4660
|
+
* } catch (error) {
|
|
4661
|
+
* if (isKitError(error)) {
|
|
4662
|
+
* // TypeScript knows this is KitError
|
|
4663
|
+
* console.log(`Structured error: ${error.name} (${error.code})`)
|
|
4664
|
+
* } else {
|
|
4665
|
+
* console.log('Regular error:', error)
|
|
4666
|
+
* }
|
|
4667
|
+
* }
|
|
4668
|
+
* ```
|
|
4669
|
+
*/
|
|
4670
|
+
function isKitError(error) {
|
|
4671
|
+
return error instanceof KitError;
|
|
4672
|
+
}
|
|
4673
|
+
|
|
4538
4674
|
const assertCCTPv2BridgeParamsSymbol = Symbol('assertCCTPv2BridgeParams');
|
|
4539
4675
|
/**
|
|
4540
4676
|
* Asserts that the provided parameters match the CCTPv2 bridge parameters interface.
|
|
@@ -4629,6 +4765,11 @@ function assertCCTPv2BridgeParams(params) {
|
|
|
4629
4765
|
// Validate CCTP v2 specific requirements for both wallets
|
|
4630
4766
|
assertCCTPv2WalletContext(bridgeParams.source);
|
|
4631
4767
|
assertCCTPv2WalletContext(bridgeParams.destination);
|
|
4768
|
+
// Validate that adapters support their respective chains (defense-in-depth)
|
|
4769
|
+
// This provides additional validation for users calling the provider directly
|
|
4770
|
+
// without going through the kit layer, ensuring early detection of unsupported chains
|
|
4771
|
+
bridgeParams.source.adapter.validateChainSupport(bridgeParams.source.chain);
|
|
4772
|
+
bridgeParams.destination.adapter.validateChainSupport(bridgeParams.destination.chain);
|
|
4632
4773
|
}
|
|
4633
4774
|
/**
|
|
4634
4775
|
* Validate CCTP v2 support on both chains
|
|
@@ -5211,7 +5352,11 @@ async function bridge(params, provider) {
|
|
|
5211
5352
|
try {
|
|
5212
5353
|
const step = await executor(params, provider, context);
|
|
5213
5354
|
if (step.state === 'error') {
|
|
5214
|
-
//
|
|
5355
|
+
// If step.error is a KitError, preserve it by re-throwing directly
|
|
5356
|
+
if (isKitError(step.error)) {
|
|
5357
|
+
throw step.error;
|
|
5358
|
+
}
|
|
5359
|
+
// For non-KitError errors, wrap in descriptive Error
|
|
5215
5360
|
let fallbackErrorMessage = 'Unknown error';
|
|
5216
5361
|
if (step.error instanceof Error) {
|
|
5217
5362
|
fallbackErrorMessage = step.error.message;
|
package/index.d.ts
CHANGED
|
@@ -1177,6 +1177,14 @@ interface CCTPv2ActionMap {
|
|
|
1177
1177
|
* the adapter's default address.
|
|
1178
1178
|
*/
|
|
1179
1179
|
readonly destinationAddress?: string;
|
|
1180
|
+
/**
|
|
1181
|
+
* The mint recipient address from the decoded CCTP message.
|
|
1182
|
+
*
|
|
1183
|
+
* This is the actual address encoded in the burn message where tokens will be minted.
|
|
1184
|
+
* For Solana, this is already the Associated Token Account (ATA) address, not the owner.
|
|
1185
|
+
* For EVM chains, this is the recipient's wallet address.
|
|
1186
|
+
*/
|
|
1187
|
+
readonly mintRecipient?: string;
|
|
1180
1188
|
};
|
|
1181
1189
|
/**
|
|
1182
1190
|
* Initiate a cross-chain USDC transfer using a custom bridge contract with preapproval funnel.
|
|
@@ -2118,10 +2126,10 @@ declare abstract class Adapter<TAdapterCapabilities extends AdapterCapabilities
|
|
|
2118
2126
|
* This address is used as the default sender for transactions
|
|
2119
2127
|
* and interactions initiated by this adapter.
|
|
2120
2128
|
*
|
|
2121
|
-
* @param chain -
|
|
2129
|
+
* @param chain - The chain to use for address resolution.
|
|
2122
2130
|
* @returns A promise that resolves to the blockchain address as a string.
|
|
2123
2131
|
*/
|
|
2124
|
-
abstract getAddress(chain
|
|
2132
|
+
abstract getAddress(chain: ChainDefinition): Promise<string>;
|
|
2125
2133
|
/**
|
|
2126
2134
|
* Switches the adapter to operate on the specified chain.
|
|
2127
2135
|
*
|
|
@@ -2192,7 +2200,7 @@ declare abstract class Adapter<TAdapterCapabilities extends AdapterCapabilities
|
|
|
2192
2200
|
* Validate that the target chain is supported by this adapter.
|
|
2193
2201
|
*
|
|
2194
2202
|
* @param targetChain - The chain to validate.
|
|
2195
|
-
* @throws
|
|
2203
|
+
* @throws KitError with INVALID_CHAIN code if the chain is not supported by this adapter.
|
|
2196
2204
|
*/
|
|
2197
2205
|
validateChainSupport(targetChain: ChainDefinition): void;
|
|
2198
2206
|
/**
|
package/index.mjs
CHANGED
|
@@ -4185,9 +4185,71 @@ const RECOVERABILITY_VALUES = [
|
|
|
4185
4185
|
'RESUMABLE',
|
|
4186
4186
|
'FATAL',
|
|
4187
4187
|
];
|
|
4188
|
+
/**
|
|
4189
|
+
* Error type constants for categorizing errors by origin.
|
|
4190
|
+
*
|
|
4191
|
+
* This const object provides a reference for error types, enabling
|
|
4192
|
+
* IDE autocomplete and preventing typos when creating custom errors.
|
|
4193
|
+
*
|
|
4194
|
+
* @remarks
|
|
4195
|
+
* While internal error definitions use string literals with type annotations
|
|
4196
|
+
* for strict type safety, this constant is useful for developers creating
|
|
4197
|
+
* custom error instances or checking error types programmatically.
|
|
4198
|
+
*
|
|
4199
|
+
* @example
|
|
4200
|
+
* ```typescript
|
|
4201
|
+
* import { ERROR_TYPES, KitError } from '@core/errors'
|
|
4202
|
+
*
|
|
4203
|
+
* // Use for type checking
|
|
4204
|
+
* if (error.type === ERROR_TYPES.BALANCE) {
|
|
4205
|
+
* console.log('This is a balance error')
|
|
4206
|
+
* }
|
|
4207
|
+
* ```
|
|
4208
|
+
*
|
|
4209
|
+
* @example
|
|
4210
|
+
* ```typescript
|
|
4211
|
+
* // Use as reference when creating custom errors
|
|
4212
|
+
* const error = new KitError({
|
|
4213
|
+
* code: 9999,
|
|
4214
|
+
* name: 'CUSTOM_ERROR',
|
|
4215
|
+
* type: ERROR_TYPES.BALANCE, // IDE autocomplete works here
|
|
4216
|
+
* recoverability: 'FATAL',
|
|
4217
|
+
* message: 'Custom balance error'
|
|
4218
|
+
* })
|
|
4219
|
+
* ```
|
|
4220
|
+
*/
|
|
4221
|
+
const ERROR_TYPES = {
|
|
4222
|
+
/** User input validation and parameter checking */
|
|
4223
|
+
INPUT: 'INPUT',
|
|
4224
|
+
/** Insufficient token balances and amount validation */
|
|
4225
|
+
BALANCE: 'BALANCE',
|
|
4226
|
+
/** On-chain execution: reverts, gas issues, transaction failures */
|
|
4227
|
+
ONCHAIN: 'ONCHAIN',
|
|
4228
|
+
/** Blockchain RPC provider issues and endpoint problems */
|
|
4229
|
+
RPC: 'RPC',
|
|
4230
|
+
/** Internet connectivity, DNS resolution, connection issues */
|
|
4231
|
+
NETWORK: 'NETWORK',
|
|
4232
|
+
};
|
|
4233
|
+
/**
|
|
4234
|
+
* Array of valid error type values for validation.
|
|
4235
|
+
* Derived from ERROR_TYPES const object.
|
|
4236
|
+
*/
|
|
4237
|
+
const ERROR_TYPE_VALUES = Object.values(ERROR_TYPES);
|
|
4188
4238
|
|
|
4189
|
-
// Create
|
|
4239
|
+
// Create mutable arrays for Zod enum validation
|
|
4190
4240
|
const RECOVERABILITY_ARRAY = [...RECOVERABILITY_VALUES];
|
|
4241
|
+
const ERROR_TYPE_ARRAY = [...ERROR_TYPE_VALUES];
|
|
4242
|
+
/**
|
|
4243
|
+
* Error code ranges for validation.
|
|
4244
|
+
* Single source of truth for valid error code ranges.
|
|
4245
|
+
*/
|
|
4246
|
+
const ERROR_CODE_RANGES = [
|
|
4247
|
+
{ min: 1000, max: 1999, type: 'INPUT' },
|
|
4248
|
+
{ min: 3000, max: 3999, type: 'NETWORK' },
|
|
4249
|
+
{ min: 4000, max: 4999, type: 'RPC' },
|
|
4250
|
+
{ min: 5000, max: 5999, type: 'ONCHAIN' },
|
|
4251
|
+
{ min: 9000, max: 9999, type: 'BALANCE' },
|
|
4252
|
+
];
|
|
4191
4253
|
/**
|
|
4192
4254
|
* Zod schema for validating ErrorDetails objects.
|
|
4193
4255
|
*
|
|
@@ -4200,7 +4262,8 @@ const RECOVERABILITY_ARRAY = [...RECOVERABILITY_VALUES];
|
|
|
4200
4262
|
*
|
|
4201
4263
|
* const result = errorDetailsSchema.safeParse({
|
|
4202
4264
|
* code: 1001,
|
|
4203
|
-
* name: '
|
|
4265
|
+
* name: 'INPUT_NETWORK_MISMATCH',
|
|
4266
|
+
* type: 'INPUT',
|
|
4204
4267
|
* recoverability: 'FATAL',
|
|
4205
4268
|
* message: 'Source and destination networks must be different'
|
|
4206
4269
|
* })
|
|
@@ -4209,30 +4272,56 @@ const RECOVERABILITY_ARRAY = [...RECOVERABILITY_VALUES];
|
|
|
4209
4272
|
* console.error('Validation failed:', result.error.issues)
|
|
4210
4273
|
* }
|
|
4211
4274
|
* ```
|
|
4275
|
+
*
|
|
4276
|
+
* @example
|
|
4277
|
+
* ```typescript
|
|
4278
|
+
* // Runtime error
|
|
4279
|
+
* const result = errorDetailsSchema.safeParse({
|
|
4280
|
+
* code: 9001,
|
|
4281
|
+
* name: 'BALANCE_INSUFFICIENT_TOKEN',
|
|
4282
|
+
* type: 'BALANCE',
|
|
4283
|
+
* recoverability: 'FATAL',
|
|
4284
|
+
* message: 'Insufficient USDC balance'
|
|
4285
|
+
* })
|
|
4286
|
+
* ```
|
|
4212
4287
|
*/
|
|
4213
4288
|
const errorDetailsSchema = z.object({
|
|
4214
|
-
/**
|
|
4289
|
+
/**
|
|
4290
|
+
* Numeric identifier following standardized ranges:
|
|
4291
|
+
* - 1000-1999: INPUT errors - Parameter validation
|
|
4292
|
+
* - 3000-3999: NETWORK errors - Connectivity issues
|
|
4293
|
+
* - 4000-4999: RPC errors - Provider issues, gas estimation
|
|
4294
|
+
* - 5000-5999: ONCHAIN errors - Transaction/simulation failures
|
|
4295
|
+
* - 9000-9999: BALANCE errors - Insufficient funds
|
|
4296
|
+
*/
|
|
4215
4297
|
code: z
|
|
4216
4298
|
.number()
|
|
4217
4299
|
.int('Error code must be an integer')
|
|
4218
|
-
.
|
|
4219
|
-
|
|
4220
|
-
|
|
4300
|
+
.refine((code) => ERROR_CODE_RANGES.some((range) => code >= range.min && code <= range.max), {
|
|
4301
|
+
message: 'Error code must be in valid ranges: 1000-1999 (INPUT), 3000-3999 (NETWORK), 4000-4999 (RPC), 5000-5999 (ONCHAIN), 9000-9999 (BALANCE)',
|
|
4302
|
+
}),
|
|
4303
|
+
/** Human-readable ID (e.g., "INPUT_NETWORK_MISMATCH", "BALANCE_INSUFFICIENT_TOKEN") */
|
|
4221
4304
|
name: z
|
|
4222
4305
|
.string()
|
|
4223
4306
|
.min(1, 'Error name must be a non-empty string')
|
|
4224
4307
|
.regex(/^[A-Z_][A-Z0-9_]*$/, 'Error name must match pattern: ^[A-Z_][A-Z0-9_]*$'),
|
|
4308
|
+
/** Error category indicating where the error originated */
|
|
4309
|
+
type: z.enum(ERROR_TYPE_ARRAY, {
|
|
4310
|
+
errorMap: () => ({
|
|
4311
|
+
message: 'Error type must be one of: INPUT, BALANCE, ONCHAIN, RPC, NETWORK',
|
|
4312
|
+
}),
|
|
4313
|
+
}),
|
|
4225
4314
|
/** Error handling strategy */
|
|
4226
4315
|
recoverability: z.enum(RECOVERABILITY_ARRAY, {
|
|
4227
4316
|
errorMap: () => ({
|
|
4228
4317
|
message: 'Recoverability must be one of: RETRYABLE, RESUMABLE, FATAL',
|
|
4229
4318
|
}),
|
|
4230
4319
|
}),
|
|
4231
|
-
/** User-friendly explanation with
|
|
4320
|
+
/** User-friendly explanation with context */
|
|
4232
4321
|
message: z
|
|
4233
4322
|
.string()
|
|
4234
4323
|
.min(1, 'Error message must be a non-empty string')
|
|
4235
|
-
.max(
|
|
4324
|
+
.max(1000, 'Error message must be 1000 characters or less'),
|
|
4236
4325
|
/** Raw error details, context, or the original error that caused this one. */
|
|
4237
4326
|
cause: z
|
|
4238
4327
|
.object({
|
|
@@ -4247,7 +4336,7 @@ const errorDetailsSchema = z.object({
|
|
|
4247
4336
|
*
|
|
4248
4337
|
* @param details - The object to validate
|
|
4249
4338
|
* @returns The validated ErrorDetails object
|
|
4250
|
-
* @throws
|
|
4339
|
+
* @throws TypeError When validation fails
|
|
4251
4340
|
*
|
|
4252
4341
|
* @example
|
|
4253
4342
|
* ```typescript
|
|
@@ -4324,6 +4413,8 @@ class KitError extends Error {
|
|
|
4324
4413
|
code;
|
|
4325
4414
|
/** Human-readable ID (e.g., "NETWORK_MISMATCH") */
|
|
4326
4415
|
name;
|
|
4416
|
+
/** Error category indicating where the error originated */
|
|
4417
|
+
type;
|
|
4327
4418
|
/** Error handling strategy */
|
|
4328
4419
|
recoverability;
|
|
4329
4420
|
/** Raw error details, context, or the original error that caused this one. */
|
|
@@ -4352,6 +4443,12 @@ class KitError extends Error {
|
|
|
4352
4443
|
enumerable: true,
|
|
4353
4444
|
configurable: false,
|
|
4354
4445
|
},
|
|
4446
|
+
type: {
|
|
4447
|
+
value: validatedDetails.type,
|
|
4448
|
+
writable: false,
|
|
4449
|
+
enumerable: true,
|
|
4450
|
+
configurable: false,
|
|
4451
|
+
},
|
|
4355
4452
|
recoverability: {
|
|
4356
4453
|
value: validatedDetails.recoverability,
|
|
4357
4454
|
writable: false,
|
|
@@ -4371,14 +4468,19 @@ class KitError extends Error {
|
|
|
4371
4468
|
}
|
|
4372
4469
|
|
|
4373
4470
|
/**
|
|
4374
|
-
*
|
|
4375
|
-
*
|
|
4471
|
+
* Standardized error code ranges for consistent categorization:
|
|
4472
|
+
*
|
|
4473
|
+
* - 1000-1999: INPUT errors - Parameter validation, input format errors
|
|
4474
|
+
* - 3000-3999: NETWORK errors - Internet connectivity, DNS, connection issues
|
|
4475
|
+
* - 4000-4999: RPC errors - Blockchain provider issues, gas estimation, nonce errors
|
|
4476
|
+
* - 5000-5999: ONCHAIN errors - Transaction/simulation failures, gas exhaustion, reverts
|
|
4477
|
+
* - 9000-9999: BALANCE errors - Insufficient funds, token balance, allowance
|
|
4376
4478
|
*/
|
|
4377
4479
|
/**
|
|
4378
4480
|
* Standardized error definitions for INPUT type errors.
|
|
4379
4481
|
*
|
|
4380
|
-
* Each entry combines the numeric error code
|
|
4381
|
-
*
|
|
4482
|
+
* Each entry combines the numeric error code, string name, and type
|
|
4483
|
+
* to ensure consistency when creating error instances.
|
|
4382
4484
|
*
|
|
4383
4485
|
* Error codes follow a hierarchical numbering scheme where the first digit
|
|
4384
4486
|
* indicates the error category (1 = INPUT) and subsequent digits provide
|
|
@@ -4395,9 +4497,10 @@ class KitError extends Error {
|
|
|
4395
4497
|
* message: 'Source and destination networks must be different'
|
|
4396
4498
|
* })
|
|
4397
4499
|
*
|
|
4398
|
-
* // Access code and
|
|
4500
|
+
* // Access code, name, and type individually if needed
|
|
4399
4501
|
* console.log(InputError.NETWORK_MISMATCH.code) // 1001
|
|
4400
4502
|
* console.log(InputError.NETWORK_MISMATCH.name) // 'INPUT_NETWORK_MISMATCH'
|
|
4503
|
+
* console.log(InputError.NETWORK_MISMATCH.type) // 'INPUT'
|
|
4401
4504
|
* ```
|
|
4402
4505
|
*/
|
|
4403
4506
|
const InputError = {
|
|
@@ -4405,16 +4508,19 @@ const InputError = {
|
|
|
4405
4508
|
NETWORK_MISMATCH: {
|
|
4406
4509
|
code: 1001,
|
|
4407
4510
|
name: 'INPUT_NETWORK_MISMATCH',
|
|
4511
|
+
type: 'INPUT',
|
|
4408
4512
|
},
|
|
4409
4513
|
/** Unsupported or invalid bridge route configuration */
|
|
4410
4514
|
UNSUPPORTED_ROUTE: {
|
|
4411
4515
|
code: 1003,
|
|
4412
4516
|
name: 'INPUT_UNSUPPORTED_ROUTE',
|
|
4517
|
+
type: 'INPUT',
|
|
4413
4518
|
},
|
|
4414
4519
|
/** General validation failure for complex validation rules */
|
|
4415
4520
|
VALIDATION_FAILED: {
|
|
4416
4521
|
code: 1098,
|
|
4417
4522
|
name: 'INPUT_VALIDATION_FAILED',
|
|
4523
|
+
type: 'INPUT',
|
|
4418
4524
|
},
|
|
4419
4525
|
};
|
|
4420
4526
|
|
|
@@ -4529,6 +4635,36 @@ function createValidationFailedError(field, value, reason) {
|
|
|
4529
4635
|
return new KitError(errorDetails);
|
|
4530
4636
|
}
|
|
4531
4637
|
|
|
4638
|
+
/**
|
|
4639
|
+
* Type guard to check if an error is a KitError instance.
|
|
4640
|
+
*
|
|
4641
|
+
* This guard enables TypeScript to narrow the type from `unknown` to
|
|
4642
|
+
* `KitError`, providing access to structured error properties like
|
|
4643
|
+
* code, name, and recoverability.
|
|
4644
|
+
*
|
|
4645
|
+
* @param error - Unknown error to check
|
|
4646
|
+
* @returns True if error is KitError with proper type narrowing
|
|
4647
|
+
*
|
|
4648
|
+
* @example
|
|
4649
|
+
* ```typescript
|
|
4650
|
+
* import { isKitError } from '@core/errors'
|
|
4651
|
+
*
|
|
4652
|
+
* try {
|
|
4653
|
+
* await kit.bridge(params)
|
|
4654
|
+
* } catch (error) {
|
|
4655
|
+
* if (isKitError(error)) {
|
|
4656
|
+
* // TypeScript knows this is KitError
|
|
4657
|
+
* console.log(`Structured error: ${error.name} (${error.code})`)
|
|
4658
|
+
* } else {
|
|
4659
|
+
* console.log('Regular error:', error)
|
|
4660
|
+
* }
|
|
4661
|
+
* }
|
|
4662
|
+
* ```
|
|
4663
|
+
*/
|
|
4664
|
+
function isKitError(error) {
|
|
4665
|
+
return error instanceof KitError;
|
|
4666
|
+
}
|
|
4667
|
+
|
|
4532
4668
|
const assertCCTPv2BridgeParamsSymbol = Symbol('assertCCTPv2BridgeParams');
|
|
4533
4669
|
/**
|
|
4534
4670
|
* Asserts that the provided parameters match the CCTPv2 bridge parameters interface.
|
|
@@ -4623,6 +4759,11 @@ function assertCCTPv2BridgeParams(params) {
|
|
|
4623
4759
|
// Validate CCTP v2 specific requirements for both wallets
|
|
4624
4760
|
assertCCTPv2WalletContext(bridgeParams.source);
|
|
4625
4761
|
assertCCTPv2WalletContext(bridgeParams.destination);
|
|
4762
|
+
// Validate that adapters support their respective chains (defense-in-depth)
|
|
4763
|
+
// This provides additional validation for users calling the provider directly
|
|
4764
|
+
// without going through the kit layer, ensuring early detection of unsupported chains
|
|
4765
|
+
bridgeParams.source.adapter.validateChainSupport(bridgeParams.source.chain);
|
|
4766
|
+
bridgeParams.destination.adapter.validateChainSupport(bridgeParams.destination.chain);
|
|
4626
4767
|
}
|
|
4627
4768
|
/**
|
|
4628
4769
|
* Validate CCTP v2 support on both chains
|
|
@@ -5205,7 +5346,11 @@ async function bridge(params, provider) {
|
|
|
5205
5346
|
try {
|
|
5206
5347
|
const step = await executor(params, provider, context);
|
|
5207
5348
|
if (step.state === 'error') {
|
|
5208
|
-
//
|
|
5349
|
+
// If step.error is a KitError, preserve it by re-throwing directly
|
|
5350
|
+
if (isKitError(step.error)) {
|
|
5351
|
+
throw step.error;
|
|
5352
|
+
}
|
|
5353
|
+
// For non-KitError errors, wrap in descriptive Error
|
|
5209
5354
|
let fallbackErrorMessage = 'Unknown error';
|
|
5210
5355
|
if (step.error instanceof Error) {
|
|
5211
5356
|
fallbackErrorMessage = step.error.message;
|
package/package.json
CHANGED