@circle-fin/bridge-kit 1.1.1 → 1.1.2

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 CHANGED
@@ -1,5 +1,20 @@
1
1
  # @circle-fin/bridge-kit
2
2
 
3
+ ## 1.1.2
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
+
3
18
  ## 1.1.1
4
19
 
5
20
  ### Patch Changes
package/index.cjs CHANGED
@@ -2741,7 +2741,7 @@ class Actionable {
2741
2741
  }
2742
2742
 
2743
2743
  var name = "@circle-fin/bridge-kit";
2744
- var version = "1.1.1";
2744
+ var version = "1.1.2";
2745
2745
  var pkg = {
2746
2746
  name: name,
2747
2747
  version: version};
@@ -2758,9 +2758,71 @@ const RECOVERABILITY_VALUES = [
2758
2758
  'RESUMABLE',
2759
2759
  'FATAL',
2760
2760
  ];
2761
+ /**
2762
+ * Error type constants for categorizing errors by origin.
2763
+ *
2764
+ * This const object provides a reference for error types, enabling
2765
+ * IDE autocomplete and preventing typos when creating custom errors.
2766
+ *
2767
+ * @remarks
2768
+ * While internal error definitions use string literals with type annotations
2769
+ * for strict type safety, this constant is useful for developers creating
2770
+ * custom error instances or checking error types programmatically.
2771
+ *
2772
+ * @example
2773
+ * ```typescript
2774
+ * import { ERROR_TYPES, KitError } from '@core/errors'
2775
+ *
2776
+ * // Use for type checking
2777
+ * if (error.type === ERROR_TYPES.BALANCE) {
2778
+ * console.log('This is a balance error')
2779
+ * }
2780
+ * ```
2781
+ *
2782
+ * @example
2783
+ * ```typescript
2784
+ * // Use as reference when creating custom errors
2785
+ * const error = new KitError({
2786
+ * code: 9999,
2787
+ * name: 'CUSTOM_ERROR',
2788
+ * type: ERROR_TYPES.BALANCE, // IDE autocomplete works here
2789
+ * recoverability: 'FATAL',
2790
+ * message: 'Custom balance error'
2791
+ * })
2792
+ * ```
2793
+ */
2794
+ const ERROR_TYPES = {
2795
+ /** User input validation and parameter checking */
2796
+ INPUT: 'INPUT',
2797
+ /** Insufficient token balances and amount validation */
2798
+ BALANCE: 'BALANCE',
2799
+ /** On-chain execution: reverts, gas issues, transaction failures */
2800
+ ONCHAIN: 'ONCHAIN',
2801
+ /** Blockchain RPC provider issues and endpoint problems */
2802
+ RPC: 'RPC',
2803
+ /** Internet connectivity, DNS resolution, connection issues */
2804
+ NETWORK: 'NETWORK',
2805
+ };
2806
+ /**
2807
+ * Array of valid error type values for validation.
2808
+ * Derived from ERROR_TYPES const object.
2809
+ */
2810
+ const ERROR_TYPE_VALUES = Object.values(ERROR_TYPES);
2761
2811
 
2762
- // Create a mutable array for Zod enum validation
2812
+ // Create mutable arrays for Zod enum validation
2763
2813
  const RECOVERABILITY_ARRAY = [...RECOVERABILITY_VALUES];
2814
+ const ERROR_TYPE_ARRAY = [...ERROR_TYPE_VALUES];
2815
+ /**
2816
+ * Error code ranges for validation.
2817
+ * Single source of truth for valid error code ranges.
2818
+ */
2819
+ const ERROR_CODE_RANGES = [
2820
+ { min: 1000, max: 1999, type: 'INPUT' },
2821
+ { min: 3000, max: 3999, type: 'NETWORK' },
2822
+ { min: 4000, max: 4999, type: 'RPC' },
2823
+ { min: 5000, max: 5999, type: 'ONCHAIN' },
2824
+ { min: 9000, max: 9999, type: 'BALANCE' },
2825
+ ];
2764
2826
  /**
2765
2827
  * Zod schema for validating ErrorDetails objects.
2766
2828
  *
@@ -2773,7 +2835,8 @@ const RECOVERABILITY_ARRAY = [...RECOVERABILITY_VALUES];
2773
2835
  *
2774
2836
  * const result = errorDetailsSchema.safeParse({
2775
2837
  * code: 1001,
2776
- * name: 'NETWORK_MISMATCH',
2838
+ * name: 'INPUT_NETWORK_MISMATCH',
2839
+ * type: 'INPUT',
2777
2840
  * recoverability: 'FATAL',
2778
2841
  * message: 'Source and destination networks must be different'
2779
2842
  * })
@@ -2782,30 +2845,56 @@ const RECOVERABILITY_ARRAY = [...RECOVERABILITY_VALUES];
2782
2845
  * console.error('Validation failed:', result.error.issues)
2783
2846
  * }
2784
2847
  * ```
2848
+ *
2849
+ * @example
2850
+ * ```typescript
2851
+ * // Runtime error
2852
+ * const result = errorDetailsSchema.safeParse({
2853
+ * code: 9001,
2854
+ * name: 'BALANCE_INSUFFICIENT_TOKEN',
2855
+ * type: 'BALANCE',
2856
+ * recoverability: 'FATAL',
2857
+ * message: 'Insufficient USDC balance'
2858
+ * })
2859
+ * ```
2785
2860
  */
2786
2861
  const errorDetailsSchema = zod.z.object({
2787
- /** Numeric identifier following standardized ranges (1000+ for INPUT errors) */
2862
+ /**
2863
+ * Numeric identifier following standardized ranges:
2864
+ * - 1000-1999: INPUT errors - Parameter validation
2865
+ * - 3000-3999: NETWORK errors - Connectivity issues
2866
+ * - 4000-4999: RPC errors - Provider issues, gas estimation
2867
+ * - 5000-5999: ONCHAIN errors - Transaction/simulation failures
2868
+ * - 9000-9999: BALANCE errors - Insufficient funds
2869
+ */
2788
2870
  code: zod.z
2789
2871
  .number()
2790
2872
  .int('Error code must be an integer')
2791
- .min(1000, 'Error code must be within valid range (1000+)')
2792
- .max(1099, 'Error code must be within valid range (1099 max)'),
2793
- /** Human-readable ID (e.g., "NETWORK_MISMATCH") */
2873
+ .refine((code) => ERROR_CODE_RANGES.some((range) => code >= range.min && code <= range.max), {
2874
+ message: 'Error code must be in valid ranges: 1000-1999 (INPUT), 3000-3999 (NETWORK), 4000-4999 (RPC), 5000-5999 (ONCHAIN), 9000-9999 (BALANCE)',
2875
+ }),
2876
+ /** Human-readable ID (e.g., "INPUT_NETWORK_MISMATCH", "BALANCE_INSUFFICIENT_TOKEN") */
2794
2877
  name: zod.z
2795
2878
  .string()
2796
2879
  .min(1, 'Error name must be a non-empty string')
2797
2880
  .regex(/^[A-Z_][A-Z0-9_]*$/, 'Error name must match pattern: ^[A-Z_][A-Z0-9_]*$'),
2881
+ /** Error category indicating where the error originated */
2882
+ type: zod.z.enum(ERROR_TYPE_ARRAY, {
2883
+ errorMap: () => ({
2884
+ message: 'Error type must be one of: INPUT, BALANCE, ONCHAIN, RPC, NETWORK',
2885
+ }),
2886
+ }),
2798
2887
  /** Error handling strategy */
2799
2888
  recoverability: zod.z.enum(RECOVERABILITY_ARRAY, {
2800
2889
  errorMap: () => ({
2801
2890
  message: 'Recoverability must be one of: RETRYABLE, RESUMABLE, FATAL',
2802
2891
  }),
2803
2892
  }),
2804
- /** User-friendly explanation with network context */
2893
+ /** User-friendly explanation with context */
2805
2894
  message: zod.z
2806
2895
  .string()
2807
2896
  .min(1, 'Error message must be a non-empty string')
2808
- .max(500, 'Error message must be 500 characters or less'),
2897
+ .max(1000, 'Error message must be 1000 characters or less'),
2809
2898
  /** Raw error details, context, or the original error that caused this one. */
2810
2899
  cause: zod.z
2811
2900
  .object({
@@ -2820,7 +2909,7 @@ const errorDetailsSchema = zod.z.object({
2820
2909
  *
2821
2910
  * @param details - The object to validate
2822
2911
  * @returns The validated ErrorDetails object
2823
- * @throws {TypeError} When validation fails
2912
+ * @throws TypeError When validation fails
2824
2913
  *
2825
2914
  * @example
2826
2915
  * ```typescript
@@ -2897,6 +2986,8 @@ class KitError extends Error {
2897
2986
  code;
2898
2987
  /** Human-readable ID (e.g., "NETWORK_MISMATCH") */
2899
2988
  name;
2989
+ /** Error category indicating where the error originated */
2990
+ type;
2900
2991
  /** Error handling strategy */
2901
2992
  recoverability;
2902
2993
  /** Raw error details, context, or the original error that caused this one. */
@@ -2925,6 +3016,12 @@ class KitError extends Error {
2925
3016
  enumerable: true,
2926
3017
  configurable: false,
2927
3018
  },
3019
+ type: {
3020
+ value: validatedDetails.type,
3021
+ writable: false,
3022
+ enumerable: true,
3023
+ configurable: false,
3024
+ },
2928
3025
  recoverability: {
2929
3026
  value: validatedDetails.recoverability,
2930
3027
  writable: false,
@@ -2944,20 +3041,19 @@ class KitError extends Error {
2944
3041
  }
2945
3042
 
2946
3043
  /**
2947
- * Minimum error code for INPUT type errors.
2948
- * INPUT errors represent validation failures and invalid parameters.
2949
- */
2950
- const INPUT_ERROR_CODE_MIN = 1000;
2951
- /**
2952
- * Maximum error code for INPUT type errors (exclusive).
2953
- * INPUT error codes range from 1000 to 1099 inclusive.
3044
+ * Standardized error code ranges for consistent categorization:
3045
+ *
3046
+ * - 1000-1999: INPUT errors - Parameter validation, input format errors
3047
+ * - 3000-3999: NETWORK errors - Internet connectivity, DNS, connection issues
3048
+ * - 4000-4999: RPC errors - Blockchain provider issues, gas estimation, nonce errors
3049
+ * - 5000-5999: ONCHAIN errors - Transaction/simulation failures, gas exhaustion, reverts
3050
+ * - 9000-9999: BALANCE errors - Insufficient funds, token balance, allowance
2954
3051
  */
2955
- const INPUT_ERROR_CODE_MAX = 1100;
2956
3052
  /**
2957
3053
  * Standardized error definitions for INPUT type errors.
2958
3054
  *
2959
- * Each entry combines the numeric error code with its corresponding
2960
- * string name to ensure consistency when creating error instances.
3055
+ * Each entry combines the numeric error code, string name, and type
3056
+ * to ensure consistency when creating error instances.
2961
3057
  *
2962
3058
  * Error codes follow a hierarchical numbering scheme where the first digit
2963
3059
  * indicates the error category (1 = INPUT) and subsequent digits provide
@@ -2974,9 +3070,10 @@ const INPUT_ERROR_CODE_MAX = 1100;
2974
3070
  * message: 'Source and destination networks must be different'
2975
3071
  * })
2976
3072
  *
2977
- * // Access code and name individually if needed
3073
+ * // Access code, name, and type individually if needed
2978
3074
  * console.log(InputError.NETWORK_MISMATCH.code) // 1001
2979
3075
  * console.log(InputError.NETWORK_MISMATCH.name) // 'INPUT_NETWORK_MISMATCH'
3076
+ * console.log(InputError.NETWORK_MISMATCH.type) // 'INPUT'
2980
3077
  * ```
2981
3078
  */
2982
3079
  const InputError = {
@@ -2984,31 +3081,37 @@ const InputError = {
2984
3081
  NETWORK_MISMATCH: {
2985
3082
  code: 1001,
2986
3083
  name: 'INPUT_NETWORK_MISMATCH',
3084
+ type: 'INPUT',
2987
3085
  },
2988
3086
  /** Invalid amount format or value (negative, zero, or malformed) */
2989
3087
  INVALID_AMOUNT: {
2990
3088
  code: 1002,
2991
3089
  name: 'INPUT_INVALID_AMOUNT',
3090
+ type: 'INPUT',
2992
3091
  },
2993
3092
  /** Unsupported or invalid bridge route configuration */
2994
3093
  UNSUPPORTED_ROUTE: {
2995
3094
  code: 1003,
2996
3095
  name: 'INPUT_UNSUPPORTED_ROUTE',
3096
+ type: 'INPUT',
2997
3097
  },
2998
3098
  /** Invalid wallet or contract address format */
2999
3099
  INVALID_ADDRESS: {
3000
3100
  code: 1004,
3001
3101
  name: 'INPUT_INVALID_ADDRESS',
3102
+ type: 'INPUT',
3002
3103
  },
3003
3104
  /** Invalid or unsupported chain identifier */
3004
3105
  INVALID_CHAIN: {
3005
3106
  code: 1005,
3006
3107
  name: 'INPUT_INVALID_CHAIN',
3108
+ type: 'INPUT',
3007
3109
  },
3008
3110
  /** General validation failure for complex validation rules */
3009
3111
  VALIDATION_FAILED: {
3010
3112
  code: 1098,
3011
3113
  name: 'INPUT_VALIDATION_FAILED',
3114
+ type: 'INPUT',
3012
3115
  },
3013
3116
  };
3014
3117
 
@@ -3254,39 +3357,31 @@ function isRetryableError(error) {
3254
3357
  return isKitError(error) && error.recoverability === 'RETRYABLE';
3255
3358
  }
3256
3359
  /**
3257
- * Combined type guard to check if error is KitError with INPUT type.
3360
+ * Type guard to check if error is KitError with INPUT type.
3258
3361
  *
3259
- * INPUT errors have error codes in the 1000-1099 range and represent
3260
- * validation failures, invalid parameters, or user input problems.
3261
- * These errors are always FATAL and require the user to correct their
3262
- * input before retrying.
3362
+ * INPUT errors represent validation failures, invalid parameters,
3363
+ * or user input problems. These errors are always FATAL and require
3364
+ * the user to correct their input before retrying.
3263
3365
  *
3264
3366
  * @param error - Unknown error to check
3265
- * @returns True if error is KitError with INPUT error code range
3367
+ * @returns True if error is KitError with INPUT type
3266
3368
  *
3267
3369
  * @example
3268
3370
  * ```typescript
3269
3371
  * import { isInputError } from '@core/errors'
3270
- * import { createBridgeKit } from '@core/bridge'
3271
- *
3272
- * async function handleBridge() {
3273
- * const kit = createBridgeKit(config)
3274
- * const params = { amount: '100', from: 'ethereum', to: 'polygon' }
3275
3372
  *
3276
- * try {
3277
- * await kit.bridge(params)
3278
- * } catch (error) {
3279
- * if (isInputError(error)) {
3280
- * console.log(`Input error: ${error.message} (code: ${error.code})`)
3281
- * }
3373
+ * try {
3374
+ * await kit.bridge(params)
3375
+ * } catch (error) {
3376
+ * if (isInputError(error)) {
3377
+ * console.log('Validation error:', error.message)
3378
+ * showValidationUI()
3282
3379
  * }
3283
3380
  * }
3284
3381
  * ```
3285
3382
  */
3286
3383
  function isInputError(error) {
3287
- return (isKitError(error) &&
3288
- error.code >= INPUT_ERROR_CODE_MIN &&
3289
- error.code < INPUT_ERROR_CODE_MAX);
3384
+ return isKitError(error) && error.type === ERROR_TYPES.INPUT;
3290
3385
  }
3291
3386
  /**
3292
3387
  * Safely extracts error message from any error type.
@@ -3572,8 +3667,8 @@ function getChainFromParams(params) {
3572
3667
  /**
3573
3668
  * Maximum length for error messages in fallback validation errors.
3574
3669
  *
3575
- * KitError enforces a 500-character limit on error messages. When creating
3576
- * fallback validation errors that combine multiple Zod issues, we use 450
3670
+ * KitError enforces a 1000-character limit on error messages. When creating
3671
+ * fallback validation errors that combine multiple Zod issues, we use 950
3577
3672
  * characters to leave a 50-character buffer for:
3578
3673
  * - The error message prefix ("Invalid bridge parameters: ")
3579
3674
  * - Potential encoding differences or formatting overhead
@@ -3582,7 +3677,7 @@ function getChainFromParams(params) {
3582
3677
  * This ensures that even with concatenated issue summaries, the final message
3583
3678
  * stays within KitError's constraints.
3584
3679
  */
3585
- const MAX_MESSAGE_LENGTH = 450;
3680
+ const MAX_MESSAGE_LENGTH = 950;
3586
3681
 
3587
3682
  /**
3588
3683
  * Converts a Zod validation error into a specific KitError instance using structured pattern matching.
@@ -4716,6 +4811,10 @@ function resolveConfig(params) {
4716
4811
  async function resolveBridgeParams(params) {
4717
4812
  const fromChain = resolveChainDefinition(params.from);
4718
4813
  const toChain = resolveChainDefinition(params.to);
4814
+ // Validate adapter chain support after resolution
4815
+ // This ensures adapters support the resolved chains before proceeding
4816
+ params.from.adapter.validateChainSupport(fromChain);
4817
+ params.to.adapter.validateChainSupport(toChain);
4719
4818
  const [fromAddress, toAddress] = await Promise.all([
4720
4819
  resolveAddress(params.from),
4721
4820
  resolveAddress(params.to),
@@ -4893,7 +4992,7 @@ class BridgeKit {
4893
4992
  async bridge(params) {
4894
4993
  // First validate the parameters
4895
4994
  assertBridgeParams(params, bridgeParamsWithChainIdentifierSchema);
4896
- // Then resolve chain definitions
4995
+ // Then resolve chain definitions (includes adapter chain support validation)
4897
4996
  const resolvedParams = await resolveBridgeParams(params);
4898
4997
  // Validate network compatibility
4899
4998
  this.validateNetworkCompatibility(resolvedParams);
@@ -4997,7 +5096,7 @@ class BridgeKit {
4997
5096
  async estimate(params) {
4998
5097
  // First validate the parameters
4999
5098
  assertBridgeParams(params, bridgeParamsWithChainIdentifierSchema);
5000
- // Then resolve chain definitions
5099
+ // Then resolve chain definitions (includes adapter chain support validation)
5001
5100
  const resolvedParams = await resolveBridgeParams(params);
5002
5101
  // Validate network compatibility
5003
5102
  this.validateNetworkCompatibility(resolvedParams);
package/index.d.ts CHANGED
@@ -1179,6 +1179,14 @@ interface CCTPv2ActionMap {
1179
1179
  * the adapter's default address.
1180
1180
  */
1181
1181
  readonly destinationAddress?: string;
1182
+ /**
1183
+ * The mint recipient address from the decoded CCTP message.
1184
+ *
1185
+ * This is the actual address encoded in the burn message where tokens will be minted.
1186
+ * For Solana, this is already the Associated Token Account (ATA) address, not the owner.
1187
+ * For EVM chains, this is the recipient's wallet address.
1188
+ */
1189
+ readonly mintRecipient?: string;
1182
1190
  };
1183
1191
  /**
1184
1192
  * Initiate a cross-chain USDC transfer using a custom bridge contract with preapproval funnel.
@@ -2120,10 +2128,10 @@ declare abstract class Adapter<TAdapterCapabilities extends AdapterCapabilities
2120
2128
  * This address is used as the default sender for transactions
2121
2129
  * and interactions initiated by this adapter.
2122
2130
  *
2123
- * @param chain - Optional chain definition for chain-specific address resolution (used by EVM adapters)
2131
+ * @param chain - The chain to use for address resolution.
2124
2132
  * @returns A promise that resolves to the blockchain address as a string.
2125
2133
  */
2126
- abstract getAddress(chain?: ChainDefinition): Promise<string>;
2134
+ abstract getAddress(chain: ChainDefinition): Promise<string>;
2127
2135
  /**
2128
2136
  * Switches the adapter to operate on the specified chain.
2129
2137
  *
@@ -2194,7 +2202,7 @@ declare abstract class Adapter<TAdapterCapabilities extends AdapterCapabilities
2194
2202
  * Validate that the target chain is supported by this adapter.
2195
2203
  *
2196
2204
  * @param targetChain - The chain to validate.
2197
- * @throws Error if the chain is not supported.
2205
+ * @throws KitError with INVALID_CHAIN code if the chain is not supported by this adapter.
2198
2206
  */
2199
2207
  validateChainSupport(targetChain: ChainDefinition): void;
2200
2208
  /**
@@ -4061,6 +4069,15 @@ declare const RECOVERABILITY_VALUES: readonly ["RETRYABLE", "RESUMABLE", "FATAL"
4061
4069
  * - RESUMABLE errors are returned when a flow fails mid-execution but can be continued
4062
4070
  */
4063
4071
  type Recoverability = (typeof RECOVERABILITY_VALUES)[number];
4072
+ /**
4073
+ * Array of valid error type values for validation.
4074
+ * Derived from ERROR_TYPES const object.
4075
+ */
4076
+ declare const ERROR_TYPE_VALUES: ("INPUT" | "BALANCE" | "ONCHAIN" | "RPC" | "NETWORK")[];
4077
+ /**
4078
+ * Error type indicating the category of the error.
4079
+ */
4080
+ type ErrorType = (typeof ERROR_TYPE_VALUES)[number];
4064
4081
  /**
4065
4082
  * Structured error details with consistent properties for programmatic handling.
4066
4083
  *
@@ -4072,7 +4089,8 @@ type Recoverability = (typeof RECOVERABILITY_VALUES)[number];
4072
4089
  * ```typescript
4073
4090
  * const error: ErrorDetails = {
4074
4091
  * code: 1001,
4075
- * name: "NETWORK_MISMATCH",
4092
+ * name: "INPUT_NETWORK_MISMATCH",
4093
+ * type: "INPUT",
4076
4094
  * recoverability: "FATAL",
4077
4095
  * message: "Source and destination networks must be different",
4078
4096
  * cause: {
@@ -4080,15 +4098,31 @@ type Recoverability = (typeof RECOVERABILITY_VALUES)[number];
4080
4098
  * }
4081
4099
  * }
4082
4100
  * ```
4101
+ *
4102
+ * @example
4103
+ * ```typescript
4104
+ * const error: ErrorDetails = {
4105
+ * code: 9001,
4106
+ * name: "BALANCE_INSUFFICIENT_TOKEN",
4107
+ * type: "BALANCE",
4108
+ * recoverability: "FATAL",
4109
+ * message: "Insufficient USDC balance on Ethereum",
4110
+ * cause: {
4111
+ * trace: { token: "USDC", chain: "Ethereum" }
4112
+ * }
4113
+ * }
4114
+ * ```
4083
4115
  */
4084
4116
  interface ErrorDetails {
4085
- /** Numeric identifier following standardized ranges (1000+ for INPUT errors) */
4117
+ /** Numeric identifier following standardized ranges (see error code registry) */
4086
4118
  code: number;
4087
- /** Human-readable ID (e.g., "NETWORK_MISMATCH") */
4119
+ /** Human-readable ID (e.g., "INPUT_NETWORK_MISMATCH", "BALANCE_INSUFFICIENT_TOKEN") */
4088
4120
  name: string;
4121
+ /** Error category indicating where the error originated */
4122
+ type: ErrorType;
4089
4123
  /** Error handling strategy */
4090
4124
  recoverability: Recoverability;
4091
- /** User-friendly explanation with network context */
4125
+ /** User-friendly explanation with context */
4092
4126
  message: string;
4093
4127
  /** Raw error details, context, or the original error that caused this one. */
4094
4128
  cause?: {
@@ -4145,6 +4179,8 @@ declare class KitError extends Error implements ErrorDetails {
4145
4179
  readonly code: number;
4146
4180
  /** Human-readable ID (e.g., "NETWORK_MISMATCH") */
4147
4181
  readonly name: string;
4182
+ /** Error category indicating where the error originated */
4183
+ readonly type: ErrorType;
4148
4184
  /** Error handling strategy */
4149
4185
  readonly recoverability: Recoverability;
4150
4186
  /** Raw error details, context, or the original error that caused this one. */
@@ -4239,31 +4275,25 @@ declare function isFatalError(error: unknown): boolean;
4239
4275
  */
4240
4276
  declare function isRetryableError(error: unknown): boolean;
4241
4277
  /**
4242
- * Combined type guard to check if error is KitError with INPUT type.
4278
+ * Type guard to check if error is KitError with INPUT type.
4243
4279
  *
4244
- * INPUT errors have error codes in the 1000-1099 range and represent
4245
- * validation failures, invalid parameters, or user input problems.
4246
- * These errors are always FATAL and require the user to correct their
4247
- * input before retrying.
4280
+ * INPUT errors represent validation failures, invalid parameters,
4281
+ * or user input problems. These errors are always FATAL and require
4282
+ * the user to correct their input before retrying.
4248
4283
  *
4249
4284
  * @param error - Unknown error to check
4250
- * @returns True if error is KitError with INPUT error code range
4285
+ * @returns True if error is KitError with INPUT type
4251
4286
  *
4252
4287
  * @example
4253
4288
  * ```typescript
4254
4289
  * import { isInputError } from '@core/errors'
4255
- * import { createBridgeKit } from '@core/bridge'
4256
4290
  *
4257
- * async function handleBridge() {
4258
- * const kit = createBridgeKit(config)
4259
- * const params = { amount: '100', from: 'ethereum', to: 'polygon' }
4260
- *
4261
- * try {
4262
- * await kit.bridge(params)
4263
- * } catch (error) {
4264
- * if (isInputError(error)) {
4265
- * console.log(`Input error: ${error.message} (code: ${error.code})`)
4266
- * }
4291
+ * try {
4292
+ * await kit.bridge(params)
4293
+ * } catch (error) {
4294
+ * if (isInputError(error)) {
4295
+ * console.log('Validation error:', error.message)
4296
+ * showValidationUI()
4267
4297
  * }
4268
4298
  * }
4269
4299
  * ```
@@ -4321,4 +4351,4 @@ declare function getErrorMessage(error: unknown): string;
4321
4351
  declare function getErrorCode(error: unknown): number | null;
4322
4352
 
4323
4353
  export { Blockchain, BridgeKit, KitError, TransferSpeed, bridgeParamsWithChainIdentifierSchema, getErrorCode, getErrorMessage, isFatalError, isInputError, isKitError, isRetryableError, setExternalPrefix };
4324
- export type { ActionHandler, AdapterContext, BridgeConfig, BridgeKitConfig, BridgeParams, BridgeResult, ChainDefinition, ChainIdentifier, CustomFeePolicy, ErrorDetails, Recoverability };
4354
+ export type { ActionHandler, AdapterContext, BridgeConfig, BridgeKitConfig, BridgeParams, BridgeResult, ChainDefinition, ChainIdentifier, CustomFeePolicy, ErrorDetails, EstimateResult, Recoverability };
package/index.mjs CHANGED
@@ -2739,7 +2739,7 @@ class Actionable {
2739
2739
  }
2740
2740
 
2741
2741
  var name = "@circle-fin/bridge-kit";
2742
- var version = "1.1.1";
2742
+ var version = "1.1.2";
2743
2743
  var pkg = {
2744
2744
  name: name,
2745
2745
  version: version};
@@ -2756,9 +2756,71 @@ const RECOVERABILITY_VALUES = [
2756
2756
  'RESUMABLE',
2757
2757
  'FATAL',
2758
2758
  ];
2759
+ /**
2760
+ * Error type constants for categorizing errors by origin.
2761
+ *
2762
+ * This const object provides a reference for error types, enabling
2763
+ * IDE autocomplete and preventing typos when creating custom errors.
2764
+ *
2765
+ * @remarks
2766
+ * While internal error definitions use string literals with type annotations
2767
+ * for strict type safety, this constant is useful for developers creating
2768
+ * custom error instances or checking error types programmatically.
2769
+ *
2770
+ * @example
2771
+ * ```typescript
2772
+ * import { ERROR_TYPES, KitError } from '@core/errors'
2773
+ *
2774
+ * // Use for type checking
2775
+ * if (error.type === ERROR_TYPES.BALANCE) {
2776
+ * console.log('This is a balance error')
2777
+ * }
2778
+ * ```
2779
+ *
2780
+ * @example
2781
+ * ```typescript
2782
+ * // Use as reference when creating custom errors
2783
+ * const error = new KitError({
2784
+ * code: 9999,
2785
+ * name: 'CUSTOM_ERROR',
2786
+ * type: ERROR_TYPES.BALANCE, // IDE autocomplete works here
2787
+ * recoverability: 'FATAL',
2788
+ * message: 'Custom balance error'
2789
+ * })
2790
+ * ```
2791
+ */
2792
+ const ERROR_TYPES = {
2793
+ /** User input validation and parameter checking */
2794
+ INPUT: 'INPUT',
2795
+ /** Insufficient token balances and amount validation */
2796
+ BALANCE: 'BALANCE',
2797
+ /** On-chain execution: reverts, gas issues, transaction failures */
2798
+ ONCHAIN: 'ONCHAIN',
2799
+ /** Blockchain RPC provider issues and endpoint problems */
2800
+ RPC: 'RPC',
2801
+ /** Internet connectivity, DNS resolution, connection issues */
2802
+ NETWORK: 'NETWORK',
2803
+ };
2804
+ /**
2805
+ * Array of valid error type values for validation.
2806
+ * Derived from ERROR_TYPES const object.
2807
+ */
2808
+ const ERROR_TYPE_VALUES = Object.values(ERROR_TYPES);
2759
2809
 
2760
- // Create a mutable array for Zod enum validation
2810
+ // Create mutable arrays for Zod enum validation
2761
2811
  const RECOVERABILITY_ARRAY = [...RECOVERABILITY_VALUES];
2812
+ const ERROR_TYPE_ARRAY = [...ERROR_TYPE_VALUES];
2813
+ /**
2814
+ * Error code ranges for validation.
2815
+ * Single source of truth for valid error code ranges.
2816
+ */
2817
+ const ERROR_CODE_RANGES = [
2818
+ { min: 1000, max: 1999, type: 'INPUT' },
2819
+ { min: 3000, max: 3999, type: 'NETWORK' },
2820
+ { min: 4000, max: 4999, type: 'RPC' },
2821
+ { min: 5000, max: 5999, type: 'ONCHAIN' },
2822
+ { min: 9000, max: 9999, type: 'BALANCE' },
2823
+ ];
2762
2824
  /**
2763
2825
  * Zod schema for validating ErrorDetails objects.
2764
2826
  *
@@ -2771,7 +2833,8 @@ const RECOVERABILITY_ARRAY = [...RECOVERABILITY_VALUES];
2771
2833
  *
2772
2834
  * const result = errorDetailsSchema.safeParse({
2773
2835
  * code: 1001,
2774
- * name: 'NETWORK_MISMATCH',
2836
+ * name: 'INPUT_NETWORK_MISMATCH',
2837
+ * type: 'INPUT',
2775
2838
  * recoverability: 'FATAL',
2776
2839
  * message: 'Source and destination networks must be different'
2777
2840
  * })
@@ -2780,30 +2843,56 @@ const RECOVERABILITY_ARRAY = [...RECOVERABILITY_VALUES];
2780
2843
  * console.error('Validation failed:', result.error.issues)
2781
2844
  * }
2782
2845
  * ```
2846
+ *
2847
+ * @example
2848
+ * ```typescript
2849
+ * // Runtime error
2850
+ * const result = errorDetailsSchema.safeParse({
2851
+ * code: 9001,
2852
+ * name: 'BALANCE_INSUFFICIENT_TOKEN',
2853
+ * type: 'BALANCE',
2854
+ * recoverability: 'FATAL',
2855
+ * message: 'Insufficient USDC balance'
2856
+ * })
2857
+ * ```
2783
2858
  */
2784
2859
  const errorDetailsSchema = z.object({
2785
- /** Numeric identifier following standardized ranges (1000+ for INPUT errors) */
2860
+ /**
2861
+ * Numeric identifier following standardized ranges:
2862
+ * - 1000-1999: INPUT errors - Parameter validation
2863
+ * - 3000-3999: NETWORK errors - Connectivity issues
2864
+ * - 4000-4999: RPC errors - Provider issues, gas estimation
2865
+ * - 5000-5999: ONCHAIN errors - Transaction/simulation failures
2866
+ * - 9000-9999: BALANCE errors - Insufficient funds
2867
+ */
2786
2868
  code: z
2787
2869
  .number()
2788
2870
  .int('Error code must be an integer')
2789
- .min(1000, 'Error code must be within valid range (1000+)')
2790
- .max(1099, 'Error code must be within valid range (1099 max)'),
2791
- /** Human-readable ID (e.g., "NETWORK_MISMATCH") */
2871
+ .refine((code) => ERROR_CODE_RANGES.some((range) => code >= range.min && code <= range.max), {
2872
+ message: 'Error code must be in valid ranges: 1000-1999 (INPUT), 3000-3999 (NETWORK), 4000-4999 (RPC), 5000-5999 (ONCHAIN), 9000-9999 (BALANCE)',
2873
+ }),
2874
+ /** Human-readable ID (e.g., "INPUT_NETWORK_MISMATCH", "BALANCE_INSUFFICIENT_TOKEN") */
2792
2875
  name: z
2793
2876
  .string()
2794
2877
  .min(1, 'Error name must be a non-empty string')
2795
2878
  .regex(/^[A-Z_][A-Z0-9_]*$/, 'Error name must match pattern: ^[A-Z_][A-Z0-9_]*$'),
2879
+ /** Error category indicating where the error originated */
2880
+ type: z.enum(ERROR_TYPE_ARRAY, {
2881
+ errorMap: () => ({
2882
+ message: 'Error type must be one of: INPUT, BALANCE, ONCHAIN, RPC, NETWORK',
2883
+ }),
2884
+ }),
2796
2885
  /** Error handling strategy */
2797
2886
  recoverability: z.enum(RECOVERABILITY_ARRAY, {
2798
2887
  errorMap: () => ({
2799
2888
  message: 'Recoverability must be one of: RETRYABLE, RESUMABLE, FATAL',
2800
2889
  }),
2801
2890
  }),
2802
- /** User-friendly explanation with network context */
2891
+ /** User-friendly explanation with context */
2803
2892
  message: z
2804
2893
  .string()
2805
2894
  .min(1, 'Error message must be a non-empty string')
2806
- .max(500, 'Error message must be 500 characters or less'),
2895
+ .max(1000, 'Error message must be 1000 characters or less'),
2807
2896
  /** Raw error details, context, or the original error that caused this one. */
2808
2897
  cause: z
2809
2898
  .object({
@@ -2818,7 +2907,7 @@ const errorDetailsSchema = z.object({
2818
2907
  *
2819
2908
  * @param details - The object to validate
2820
2909
  * @returns The validated ErrorDetails object
2821
- * @throws {TypeError} When validation fails
2910
+ * @throws TypeError When validation fails
2822
2911
  *
2823
2912
  * @example
2824
2913
  * ```typescript
@@ -2895,6 +2984,8 @@ class KitError extends Error {
2895
2984
  code;
2896
2985
  /** Human-readable ID (e.g., "NETWORK_MISMATCH") */
2897
2986
  name;
2987
+ /** Error category indicating where the error originated */
2988
+ type;
2898
2989
  /** Error handling strategy */
2899
2990
  recoverability;
2900
2991
  /** Raw error details, context, or the original error that caused this one. */
@@ -2923,6 +3014,12 @@ class KitError extends Error {
2923
3014
  enumerable: true,
2924
3015
  configurable: false,
2925
3016
  },
3017
+ type: {
3018
+ value: validatedDetails.type,
3019
+ writable: false,
3020
+ enumerable: true,
3021
+ configurable: false,
3022
+ },
2926
3023
  recoverability: {
2927
3024
  value: validatedDetails.recoverability,
2928
3025
  writable: false,
@@ -2942,20 +3039,19 @@ class KitError extends Error {
2942
3039
  }
2943
3040
 
2944
3041
  /**
2945
- * Minimum error code for INPUT type errors.
2946
- * INPUT errors represent validation failures and invalid parameters.
2947
- */
2948
- const INPUT_ERROR_CODE_MIN = 1000;
2949
- /**
2950
- * Maximum error code for INPUT type errors (exclusive).
2951
- * INPUT error codes range from 1000 to 1099 inclusive.
3042
+ * Standardized error code ranges for consistent categorization:
3043
+ *
3044
+ * - 1000-1999: INPUT errors - Parameter validation, input format errors
3045
+ * - 3000-3999: NETWORK errors - Internet connectivity, DNS, connection issues
3046
+ * - 4000-4999: RPC errors - Blockchain provider issues, gas estimation, nonce errors
3047
+ * - 5000-5999: ONCHAIN errors - Transaction/simulation failures, gas exhaustion, reverts
3048
+ * - 9000-9999: BALANCE errors - Insufficient funds, token balance, allowance
2952
3049
  */
2953
- const INPUT_ERROR_CODE_MAX = 1100;
2954
3050
  /**
2955
3051
  * Standardized error definitions for INPUT type errors.
2956
3052
  *
2957
- * Each entry combines the numeric error code with its corresponding
2958
- * string name to ensure consistency when creating error instances.
3053
+ * Each entry combines the numeric error code, string name, and type
3054
+ * to ensure consistency when creating error instances.
2959
3055
  *
2960
3056
  * Error codes follow a hierarchical numbering scheme where the first digit
2961
3057
  * indicates the error category (1 = INPUT) and subsequent digits provide
@@ -2972,9 +3068,10 @@ const INPUT_ERROR_CODE_MAX = 1100;
2972
3068
  * message: 'Source and destination networks must be different'
2973
3069
  * })
2974
3070
  *
2975
- * // Access code and name individually if needed
3071
+ * // Access code, name, and type individually if needed
2976
3072
  * console.log(InputError.NETWORK_MISMATCH.code) // 1001
2977
3073
  * console.log(InputError.NETWORK_MISMATCH.name) // 'INPUT_NETWORK_MISMATCH'
3074
+ * console.log(InputError.NETWORK_MISMATCH.type) // 'INPUT'
2978
3075
  * ```
2979
3076
  */
2980
3077
  const InputError = {
@@ -2982,31 +3079,37 @@ const InputError = {
2982
3079
  NETWORK_MISMATCH: {
2983
3080
  code: 1001,
2984
3081
  name: 'INPUT_NETWORK_MISMATCH',
3082
+ type: 'INPUT',
2985
3083
  },
2986
3084
  /** Invalid amount format or value (negative, zero, or malformed) */
2987
3085
  INVALID_AMOUNT: {
2988
3086
  code: 1002,
2989
3087
  name: 'INPUT_INVALID_AMOUNT',
3088
+ type: 'INPUT',
2990
3089
  },
2991
3090
  /** Unsupported or invalid bridge route configuration */
2992
3091
  UNSUPPORTED_ROUTE: {
2993
3092
  code: 1003,
2994
3093
  name: 'INPUT_UNSUPPORTED_ROUTE',
3094
+ type: 'INPUT',
2995
3095
  },
2996
3096
  /** Invalid wallet or contract address format */
2997
3097
  INVALID_ADDRESS: {
2998
3098
  code: 1004,
2999
3099
  name: 'INPUT_INVALID_ADDRESS',
3100
+ type: 'INPUT',
3000
3101
  },
3001
3102
  /** Invalid or unsupported chain identifier */
3002
3103
  INVALID_CHAIN: {
3003
3104
  code: 1005,
3004
3105
  name: 'INPUT_INVALID_CHAIN',
3106
+ type: 'INPUT',
3005
3107
  },
3006
3108
  /** General validation failure for complex validation rules */
3007
3109
  VALIDATION_FAILED: {
3008
3110
  code: 1098,
3009
3111
  name: 'INPUT_VALIDATION_FAILED',
3112
+ type: 'INPUT',
3010
3113
  },
3011
3114
  };
3012
3115
 
@@ -3252,39 +3355,31 @@ function isRetryableError(error) {
3252
3355
  return isKitError(error) && error.recoverability === 'RETRYABLE';
3253
3356
  }
3254
3357
  /**
3255
- * Combined type guard to check if error is KitError with INPUT type.
3358
+ * Type guard to check if error is KitError with INPUT type.
3256
3359
  *
3257
- * INPUT errors have error codes in the 1000-1099 range and represent
3258
- * validation failures, invalid parameters, or user input problems.
3259
- * These errors are always FATAL and require the user to correct their
3260
- * input before retrying.
3360
+ * INPUT errors represent validation failures, invalid parameters,
3361
+ * or user input problems. These errors are always FATAL and require
3362
+ * the user to correct their input before retrying.
3261
3363
  *
3262
3364
  * @param error - Unknown error to check
3263
- * @returns True if error is KitError with INPUT error code range
3365
+ * @returns True if error is KitError with INPUT type
3264
3366
  *
3265
3367
  * @example
3266
3368
  * ```typescript
3267
3369
  * import { isInputError } from '@core/errors'
3268
- * import { createBridgeKit } from '@core/bridge'
3269
- *
3270
- * async function handleBridge() {
3271
- * const kit = createBridgeKit(config)
3272
- * const params = { amount: '100', from: 'ethereum', to: 'polygon' }
3273
3370
  *
3274
- * try {
3275
- * await kit.bridge(params)
3276
- * } catch (error) {
3277
- * if (isInputError(error)) {
3278
- * console.log(`Input error: ${error.message} (code: ${error.code})`)
3279
- * }
3371
+ * try {
3372
+ * await kit.bridge(params)
3373
+ * } catch (error) {
3374
+ * if (isInputError(error)) {
3375
+ * console.log('Validation error:', error.message)
3376
+ * showValidationUI()
3280
3377
  * }
3281
3378
  * }
3282
3379
  * ```
3283
3380
  */
3284
3381
  function isInputError(error) {
3285
- return (isKitError(error) &&
3286
- error.code >= INPUT_ERROR_CODE_MIN &&
3287
- error.code < INPUT_ERROR_CODE_MAX);
3382
+ return isKitError(error) && error.type === ERROR_TYPES.INPUT;
3288
3383
  }
3289
3384
  /**
3290
3385
  * Safely extracts error message from any error type.
@@ -3570,8 +3665,8 @@ function getChainFromParams(params) {
3570
3665
  /**
3571
3666
  * Maximum length for error messages in fallback validation errors.
3572
3667
  *
3573
- * KitError enforces a 500-character limit on error messages. When creating
3574
- * fallback validation errors that combine multiple Zod issues, we use 450
3668
+ * KitError enforces a 1000-character limit on error messages. When creating
3669
+ * fallback validation errors that combine multiple Zod issues, we use 950
3575
3670
  * characters to leave a 50-character buffer for:
3576
3671
  * - The error message prefix ("Invalid bridge parameters: ")
3577
3672
  * - Potential encoding differences or formatting overhead
@@ -3580,7 +3675,7 @@ function getChainFromParams(params) {
3580
3675
  * This ensures that even with concatenated issue summaries, the final message
3581
3676
  * stays within KitError's constraints.
3582
3677
  */
3583
- const MAX_MESSAGE_LENGTH = 450;
3678
+ const MAX_MESSAGE_LENGTH = 950;
3584
3679
 
3585
3680
  /**
3586
3681
  * Converts a Zod validation error into a specific KitError instance using structured pattern matching.
@@ -4714,6 +4809,10 @@ function resolveConfig(params) {
4714
4809
  async function resolveBridgeParams(params) {
4715
4810
  const fromChain = resolveChainDefinition(params.from);
4716
4811
  const toChain = resolveChainDefinition(params.to);
4812
+ // Validate adapter chain support after resolution
4813
+ // This ensures adapters support the resolved chains before proceeding
4814
+ params.from.adapter.validateChainSupport(fromChain);
4815
+ params.to.adapter.validateChainSupport(toChain);
4717
4816
  const [fromAddress, toAddress] = await Promise.all([
4718
4817
  resolveAddress(params.from),
4719
4818
  resolveAddress(params.to),
@@ -4891,7 +4990,7 @@ class BridgeKit {
4891
4990
  async bridge(params) {
4892
4991
  // First validate the parameters
4893
4992
  assertBridgeParams(params, bridgeParamsWithChainIdentifierSchema);
4894
- // Then resolve chain definitions
4993
+ // Then resolve chain definitions (includes adapter chain support validation)
4895
4994
  const resolvedParams = await resolveBridgeParams(params);
4896
4995
  // Validate network compatibility
4897
4996
  this.validateNetworkCompatibility(resolvedParams);
@@ -4995,7 +5094,7 @@ class BridgeKit {
4995
5094
  async estimate(params) {
4996
5095
  // First validate the parameters
4997
5096
  assertBridgeParams(params, bridgeParamsWithChainIdentifierSchema);
4998
- // Then resolve chain definitions
5097
+ // Then resolve chain definitions (includes adapter chain support validation)
4999
5098
  const resolvedParams = await resolveBridgeParams(params);
5000
5099
  // Validate network compatibility
5001
5100
  this.validateNetworkCompatibility(resolvedParams);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@circle-fin/bridge-kit",
3
- "version": "1.1.1",
3
+ "version": "1.1.2",
4
4
  "description": "SDK for seamless cross-chain stablecoin bridging",
5
5
  "engines": {
6
6
  "node": ">=16.0.0"
@@ -10,7 +10,7 @@
10
10
  "types": "./index.d.ts",
11
11
  "dependencies": {
12
12
  "zod": "3.25.67",
13
- "@circle-fin/provider-cctp-v2": "^1.0.2",
13
+ "@circle-fin/provider-cctp-v2": "^1.0.4",
14
14
  "abitype": "^1.1.0",
15
15
  "@solana/web3.js": "^1.98.4",
16
16
  "@ethersproject/address": "^5.8.0",