@circle-fin/adapter-ethers-v6 1.1.0 → 1.1.1
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 +16 -0
- package/README.md +41 -29
- package/index.cjs +728 -57
- package/index.d.ts +13 -14
- package/index.mjs +728 -57
- package/package.json +1 -1
package/index.cjs
CHANGED
|
@@ -2851,9 +2851,71 @@ const RECOVERABILITY_VALUES = [
|
|
|
2851
2851
|
'RESUMABLE',
|
|
2852
2852
|
'FATAL',
|
|
2853
2853
|
];
|
|
2854
|
+
/**
|
|
2855
|
+
* Error type constants for categorizing errors by origin.
|
|
2856
|
+
*
|
|
2857
|
+
* This const object provides a reference for error types, enabling
|
|
2858
|
+
* IDE autocomplete and preventing typos when creating custom errors.
|
|
2859
|
+
*
|
|
2860
|
+
* @remarks
|
|
2861
|
+
* While internal error definitions use string literals with type annotations
|
|
2862
|
+
* for strict type safety, this constant is useful for developers creating
|
|
2863
|
+
* custom error instances or checking error types programmatically.
|
|
2864
|
+
*
|
|
2865
|
+
* @example
|
|
2866
|
+
* ```typescript
|
|
2867
|
+
* import { ERROR_TYPES, KitError } from '@core/errors'
|
|
2868
|
+
*
|
|
2869
|
+
* // Use for type checking
|
|
2870
|
+
* if (error.type === ERROR_TYPES.BALANCE) {
|
|
2871
|
+
* console.log('This is a balance error')
|
|
2872
|
+
* }
|
|
2873
|
+
* ```
|
|
2874
|
+
*
|
|
2875
|
+
* @example
|
|
2876
|
+
* ```typescript
|
|
2877
|
+
* // Use as reference when creating custom errors
|
|
2878
|
+
* const error = new KitError({
|
|
2879
|
+
* code: 9999,
|
|
2880
|
+
* name: 'CUSTOM_ERROR',
|
|
2881
|
+
* type: ERROR_TYPES.BALANCE, // IDE autocomplete works here
|
|
2882
|
+
* recoverability: 'FATAL',
|
|
2883
|
+
* message: 'Custom balance error'
|
|
2884
|
+
* })
|
|
2885
|
+
* ```
|
|
2886
|
+
*/
|
|
2887
|
+
const ERROR_TYPES = {
|
|
2888
|
+
/** User input validation and parameter checking */
|
|
2889
|
+
INPUT: 'INPUT',
|
|
2890
|
+
/** Insufficient token balances and amount validation */
|
|
2891
|
+
BALANCE: 'BALANCE',
|
|
2892
|
+
/** On-chain execution: reverts, gas issues, transaction failures */
|
|
2893
|
+
ONCHAIN: 'ONCHAIN',
|
|
2894
|
+
/** Blockchain RPC provider issues and endpoint problems */
|
|
2895
|
+
RPC: 'RPC',
|
|
2896
|
+
/** Internet connectivity, DNS resolution, connection issues */
|
|
2897
|
+
NETWORK: 'NETWORK',
|
|
2898
|
+
};
|
|
2899
|
+
/**
|
|
2900
|
+
* Array of valid error type values for validation.
|
|
2901
|
+
* Derived from ERROR_TYPES const object.
|
|
2902
|
+
*/
|
|
2903
|
+
const ERROR_TYPE_VALUES = Object.values(ERROR_TYPES);
|
|
2854
2904
|
|
|
2855
|
-
// Create
|
|
2905
|
+
// Create mutable arrays for Zod enum validation
|
|
2856
2906
|
const RECOVERABILITY_ARRAY = [...RECOVERABILITY_VALUES];
|
|
2907
|
+
const ERROR_TYPE_ARRAY = [...ERROR_TYPE_VALUES];
|
|
2908
|
+
/**
|
|
2909
|
+
* Error code ranges for validation.
|
|
2910
|
+
* Single source of truth for valid error code ranges.
|
|
2911
|
+
*/
|
|
2912
|
+
const ERROR_CODE_RANGES = [
|
|
2913
|
+
{ min: 1000, max: 1999, type: 'INPUT' },
|
|
2914
|
+
{ min: 3000, max: 3999, type: 'NETWORK' },
|
|
2915
|
+
{ min: 4000, max: 4999, type: 'RPC' },
|
|
2916
|
+
{ min: 5000, max: 5999, type: 'ONCHAIN' },
|
|
2917
|
+
{ min: 9000, max: 9999, type: 'BALANCE' },
|
|
2918
|
+
];
|
|
2857
2919
|
/**
|
|
2858
2920
|
* Zod schema for validating ErrorDetails objects.
|
|
2859
2921
|
*
|
|
@@ -2866,7 +2928,8 @@ const RECOVERABILITY_ARRAY = [...RECOVERABILITY_VALUES];
|
|
|
2866
2928
|
*
|
|
2867
2929
|
* const result = errorDetailsSchema.safeParse({
|
|
2868
2930
|
* code: 1001,
|
|
2869
|
-
* name: '
|
|
2931
|
+
* name: 'INPUT_NETWORK_MISMATCH',
|
|
2932
|
+
* type: 'INPUT',
|
|
2870
2933
|
* recoverability: 'FATAL',
|
|
2871
2934
|
* message: 'Source and destination networks must be different'
|
|
2872
2935
|
* })
|
|
@@ -2875,30 +2938,56 @@ const RECOVERABILITY_ARRAY = [...RECOVERABILITY_VALUES];
|
|
|
2875
2938
|
* console.error('Validation failed:', result.error.issues)
|
|
2876
2939
|
* }
|
|
2877
2940
|
* ```
|
|
2941
|
+
*
|
|
2942
|
+
* @example
|
|
2943
|
+
* ```typescript
|
|
2944
|
+
* // Runtime error
|
|
2945
|
+
* const result = errorDetailsSchema.safeParse({
|
|
2946
|
+
* code: 9001,
|
|
2947
|
+
* name: 'BALANCE_INSUFFICIENT_TOKEN',
|
|
2948
|
+
* type: 'BALANCE',
|
|
2949
|
+
* recoverability: 'FATAL',
|
|
2950
|
+
* message: 'Insufficient USDC balance'
|
|
2951
|
+
* })
|
|
2952
|
+
* ```
|
|
2878
2953
|
*/
|
|
2879
2954
|
const errorDetailsSchema = zod.z.object({
|
|
2880
|
-
/**
|
|
2955
|
+
/**
|
|
2956
|
+
* Numeric identifier following standardized ranges:
|
|
2957
|
+
* - 1000-1999: INPUT errors - Parameter validation
|
|
2958
|
+
* - 3000-3999: NETWORK errors - Connectivity issues
|
|
2959
|
+
* - 4000-4999: RPC errors - Provider issues, gas estimation
|
|
2960
|
+
* - 5000-5999: ONCHAIN errors - Transaction/simulation failures
|
|
2961
|
+
* - 9000-9999: BALANCE errors - Insufficient funds
|
|
2962
|
+
*/
|
|
2881
2963
|
code: zod.z
|
|
2882
2964
|
.number()
|
|
2883
2965
|
.int('Error code must be an integer')
|
|
2884
|
-
.
|
|
2885
|
-
|
|
2886
|
-
|
|
2966
|
+
.refine((code) => ERROR_CODE_RANGES.some((range) => code >= range.min && code <= range.max), {
|
|
2967
|
+
message: 'Error code must be in valid ranges: 1000-1999 (INPUT), 3000-3999 (NETWORK), 4000-4999 (RPC), 5000-5999 (ONCHAIN), 9000-9999 (BALANCE)',
|
|
2968
|
+
}),
|
|
2969
|
+
/** Human-readable ID (e.g., "INPUT_NETWORK_MISMATCH", "BALANCE_INSUFFICIENT_TOKEN") */
|
|
2887
2970
|
name: zod.z
|
|
2888
2971
|
.string()
|
|
2889
2972
|
.min(1, 'Error name must be a non-empty string')
|
|
2890
2973
|
.regex(/^[A-Z_][A-Z0-9_]*$/, 'Error name must match pattern: ^[A-Z_][A-Z0-9_]*$'),
|
|
2974
|
+
/** Error category indicating where the error originated */
|
|
2975
|
+
type: zod.z.enum(ERROR_TYPE_ARRAY, {
|
|
2976
|
+
errorMap: () => ({
|
|
2977
|
+
message: 'Error type must be one of: INPUT, BALANCE, ONCHAIN, RPC, NETWORK',
|
|
2978
|
+
}),
|
|
2979
|
+
}),
|
|
2891
2980
|
/** Error handling strategy */
|
|
2892
2981
|
recoverability: zod.z.enum(RECOVERABILITY_ARRAY, {
|
|
2893
2982
|
errorMap: () => ({
|
|
2894
2983
|
message: 'Recoverability must be one of: RETRYABLE, RESUMABLE, FATAL',
|
|
2895
2984
|
}),
|
|
2896
2985
|
}),
|
|
2897
|
-
/** User-friendly explanation with
|
|
2986
|
+
/** User-friendly explanation with context */
|
|
2898
2987
|
message: zod.z
|
|
2899
2988
|
.string()
|
|
2900
2989
|
.min(1, 'Error message must be a non-empty string')
|
|
2901
|
-
.max(
|
|
2990
|
+
.max(1000, 'Error message must be 1000 characters or less'),
|
|
2902
2991
|
/** Raw error details, context, or the original error that caused this one. */
|
|
2903
2992
|
cause: zod.z
|
|
2904
2993
|
.object({
|
|
@@ -2913,7 +3002,7 @@ const errorDetailsSchema = zod.z.object({
|
|
|
2913
3002
|
*
|
|
2914
3003
|
* @param details - The object to validate
|
|
2915
3004
|
* @returns The validated ErrorDetails object
|
|
2916
|
-
* @throws
|
|
3005
|
+
* @throws TypeError When validation fails
|
|
2917
3006
|
*
|
|
2918
3007
|
* @example
|
|
2919
3008
|
* ```typescript
|
|
@@ -2990,6 +3079,8 @@ class KitError extends Error {
|
|
|
2990
3079
|
code;
|
|
2991
3080
|
/** Human-readable ID (e.g., "NETWORK_MISMATCH") */
|
|
2992
3081
|
name;
|
|
3082
|
+
/** Error category indicating where the error originated */
|
|
3083
|
+
type;
|
|
2993
3084
|
/** Error handling strategy */
|
|
2994
3085
|
recoverability;
|
|
2995
3086
|
/** Raw error details, context, or the original error that caused this one. */
|
|
@@ -3018,6 +3109,12 @@ class KitError extends Error {
|
|
|
3018
3109
|
enumerable: true,
|
|
3019
3110
|
configurable: false,
|
|
3020
3111
|
},
|
|
3112
|
+
type: {
|
|
3113
|
+
value: validatedDetails.type,
|
|
3114
|
+
writable: false,
|
|
3115
|
+
enumerable: true,
|
|
3116
|
+
configurable: false,
|
|
3117
|
+
},
|
|
3021
3118
|
recoverability: {
|
|
3022
3119
|
value: validatedDetails.recoverability,
|
|
3023
3120
|
writable: false,
|
|
@@ -3037,14 +3134,19 @@ class KitError extends Error {
|
|
|
3037
3134
|
}
|
|
3038
3135
|
|
|
3039
3136
|
/**
|
|
3040
|
-
*
|
|
3041
|
-
*
|
|
3137
|
+
* Standardized error code ranges for consistent categorization:
|
|
3138
|
+
*
|
|
3139
|
+
* - 1000-1999: INPUT errors - Parameter validation, input format errors
|
|
3140
|
+
* - 3000-3999: NETWORK errors - Internet connectivity, DNS, connection issues
|
|
3141
|
+
* - 4000-4999: RPC errors - Blockchain provider issues, gas estimation, nonce errors
|
|
3142
|
+
* - 5000-5999: ONCHAIN errors - Transaction/simulation failures, gas exhaustion, reverts
|
|
3143
|
+
* - 9000-9999: BALANCE errors - Insufficient funds, token balance, allowance
|
|
3042
3144
|
*/
|
|
3043
3145
|
/**
|
|
3044
3146
|
* Standardized error definitions for INPUT type errors.
|
|
3045
3147
|
*
|
|
3046
|
-
* Each entry combines the numeric error code
|
|
3047
|
-
*
|
|
3148
|
+
* Each entry combines the numeric error code, string name, and type
|
|
3149
|
+
* to ensure consistency when creating error instances.
|
|
3048
3150
|
*
|
|
3049
3151
|
* Error codes follow a hierarchical numbering scheme where the first digit
|
|
3050
3152
|
* indicates the error category (1 = INPUT) and subsequent digits provide
|
|
@@ -3061,48 +3163,609 @@ class KitError extends Error {
|
|
|
3061
3163
|
* message: 'Source and destination networks must be different'
|
|
3062
3164
|
* })
|
|
3063
3165
|
*
|
|
3064
|
-
* // Access code and
|
|
3166
|
+
* // Access code, name, and type individually if needed
|
|
3065
3167
|
* console.log(InputError.NETWORK_MISMATCH.code) // 1001
|
|
3066
3168
|
* console.log(InputError.NETWORK_MISMATCH.name) // 'INPUT_NETWORK_MISMATCH'
|
|
3169
|
+
* console.log(InputError.NETWORK_MISMATCH.type) // 'INPUT'
|
|
3067
3170
|
* ```
|
|
3068
3171
|
*/
|
|
3069
3172
|
const InputError = {
|
|
3070
|
-
/**
|
|
3071
|
-
|
|
3072
|
-
code:
|
|
3073
|
-
name: '
|
|
3173
|
+
/** Invalid or unsupported chain identifier */
|
|
3174
|
+
INVALID_CHAIN: {
|
|
3175
|
+
code: 1005,
|
|
3176
|
+
name: 'INPUT_INVALID_CHAIN',
|
|
3177
|
+
type: 'INPUT',
|
|
3178
|
+
}};
|
|
3179
|
+
/**
|
|
3180
|
+
* Standardized error definitions for BALANCE type errors.
|
|
3181
|
+
*
|
|
3182
|
+
* BALANCE errors indicate insufficient funds or allowance issues
|
|
3183
|
+
* that prevent transaction execution.
|
|
3184
|
+
*
|
|
3185
|
+
* @example
|
|
3186
|
+
* ```typescript
|
|
3187
|
+
* import { BalanceError } from '@core/errors'
|
|
3188
|
+
*
|
|
3189
|
+
* const error = new KitError({
|
|
3190
|
+
* ...BalanceError.INSUFFICIENT_TOKEN,
|
|
3191
|
+
* recoverability: 'FATAL',
|
|
3192
|
+
* message: 'Insufficient USDC balance on Ethereum',
|
|
3193
|
+
* cause: { trace: { required: '100', available: '50' } }
|
|
3194
|
+
* })
|
|
3195
|
+
* ```
|
|
3196
|
+
*/
|
|
3197
|
+
const BalanceError = {
|
|
3198
|
+
/** Insufficient token balance for transaction */
|
|
3199
|
+
INSUFFICIENT_TOKEN: {
|
|
3200
|
+
code: 9001,
|
|
3201
|
+
name: 'BALANCE_INSUFFICIENT_TOKEN',
|
|
3202
|
+
type: 'BALANCE',
|
|
3203
|
+
},
|
|
3204
|
+
/** Insufficient native token (ETH/SOL/etc) for gas fees */
|
|
3205
|
+
INSUFFICIENT_GAS: {
|
|
3206
|
+
code: 9002,
|
|
3207
|
+
name: 'BALANCE_INSUFFICIENT_GAS',
|
|
3208
|
+
type: 'BALANCE',
|
|
3209
|
+
}};
|
|
3210
|
+
/**
|
|
3211
|
+
* Standardized error definitions for ONCHAIN type errors.
|
|
3212
|
+
*
|
|
3213
|
+
* ONCHAIN errors occur during transaction execution, simulation,
|
|
3214
|
+
* or interaction with smart contracts on the blockchain.
|
|
3215
|
+
*
|
|
3216
|
+
* @example
|
|
3217
|
+
* ```typescript
|
|
3218
|
+
* import { OnchainError } from '@core/errors'
|
|
3219
|
+
*
|
|
3220
|
+
* const error = new KitError({
|
|
3221
|
+
* ...OnchainError.SIMULATION_FAILED,
|
|
3222
|
+
* recoverability: 'FATAL',
|
|
3223
|
+
* message: 'Simulation failed: ERC20 transfer amount exceeds balance',
|
|
3224
|
+
* cause: { trace: { reason: 'ERC20: transfer amount exceeds balance' } }
|
|
3225
|
+
* })
|
|
3226
|
+
* ```
|
|
3227
|
+
*/
|
|
3228
|
+
const OnchainError = {
|
|
3229
|
+
/** Transaction reverted on-chain after execution */
|
|
3230
|
+
TRANSACTION_REVERTED: {
|
|
3231
|
+
code: 5001,
|
|
3232
|
+
name: 'ONCHAIN_TRANSACTION_REVERTED',
|
|
3233
|
+
type: 'ONCHAIN',
|
|
3234
|
+
},
|
|
3235
|
+
/** Pre-flight transaction simulation failed */
|
|
3236
|
+
SIMULATION_FAILED: {
|
|
3237
|
+
code: 5002,
|
|
3238
|
+
name: 'ONCHAIN_SIMULATION_FAILED',
|
|
3239
|
+
type: 'ONCHAIN',
|
|
3240
|
+
},
|
|
3241
|
+
/** Transaction ran out of gas during execution */
|
|
3242
|
+
OUT_OF_GAS: {
|
|
3243
|
+
code: 5003,
|
|
3244
|
+
name: 'ONCHAIN_OUT_OF_GAS',
|
|
3245
|
+
type: 'ONCHAIN',
|
|
3246
|
+
}};
|
|
3247
|
+
/**
|
|
3248
|
+
* Standardized error definitions for RPC type errors.
|
|
3249
|
+
*
|
|
3250
|
+
* RPC errors occur when communicating with blockchain RPC providers,
|
|
3251
|
+
* including endpoint failures, invalid responses, and provider-specific issues.
|
|
3252
|
+
*
|
|
3253
|
+
* @example
|
|
3254
|
+
* ```typescript
|
|
3255
|
+
* import { RpcError } from '@core/errors'
|
|
3256
|
+
*
|
|
3257
|
+
* const error = new KitError({
|
|
3258
|
+
* ...RpcError.ENDPOINT_ERROR,
|
|
3259
|
+
* recoverability: 'RETRYABLE',
|
|
3260
|
+
* message: 'RPC endpoint unavailable on Ethereum',
|
|
3261
|
+
* cause: { trace: { endpoint: 'https://mainnet.infura.io' } }
|
|
3262
|
+
* })
|
|
3263
|
+
* ```
|
|
3264
|
+
*/
|
|
3265
|
+
const RpcError = {
|
|
3266
|
+
/** RPC endpoint returned error or is unavailable */
|
|
3267
|
+
ENDPOINT_ERROR: {
|
|
3268
|
+
code: 4001,
|
|
3269
|
+
name: 'RPC_ENDPOINT_ERROR',
|
|
3270
|
+
type: 'RPC',
|
|
3271
|
+
}};
|
|
3272
|
+
/**
|
|
3273
|
+
* Standardized error definitions for NETWORK type errors.
|
|
3274
|
+
*
|
|
3275
|
+
* NETWORK errors indicate connectivity issues at the network layer,
|
|
3276
|
+
* including DNS failures, connection timeouts, and unreachable endpoints.
|
|
3277
|
+
*
|
|
3278
|
+
* @example
|
|
3279
|
+
* ```typescript
|
|
3280
|
+
* import { NetworkError } from '@core/errors'
|
|
3281
|
+
*
|
|
3282
|
+
* const error = new KitError({
|
|
3283
|
+
* ...NetworkError.CONNECTION_FAILED,
|
|
3284
|
+
* recoverability: 'RETRYABLE',
|
|
3285
|
+
* message: 'Failed to connect to Ethereum network',
|
|
3286
|
+
* cause: { trace: { error: 'ECONNREFUSED' } }
|
|
3287
|
+
* })
|
|
3288
|
+
* ```
|
|
3289
|
+
*/
|
|
3290
|
+
const NetworkError = {
|
|
3291
|
+
/** Network connection failed or unreachable */
|
|
3292
|
+
CONNECTION_FAILED: {
|
|
3293
|
+
code: 3001,
|
|
3294
|
+
name: 'NETWORK_CONNECTION_FAILED',
|
|
3295
|
+
type: 'NETWORK',
|
|
3074
3296
|
}};
|
|
3075
3297
|
|
|
3076
3298
|
/**
|
|
3077
|
-
* Creates error for
|
|
3299
|
+
* Creates error for invalid chain configuration.
|
|
3078
3300
|
*
|
|
3079
|
-
* This error is thrown when
|
|
3080
|
-
*
|
|
3301
|
+
* This error is thrown when the provided chain doesn't meet the required
|
|
3302
|
+
* configuration or is not supported for the operation.
|
|
3081
3303
|
*
|
|
3082
|
-
* @param
|
|
3083
|
-
* @param
|
|
3084
|
-
* @returns KitError with
|
|
3304
|
+
* @param chain - The invalid chain name or identifier
|
|
3305
|
+
* @param reason - Specific reason why chain is invalid
|
|
3306
|
+
* @returns KitError with chain details and validation rule
|
|
3085
3307
|
*
|
|
3086
3308
|
* @example
|
|
3087
3309
|
* ```typescript
|
|
3088
|
-
* import {
|
|
3310
|
+
* import { createInvalidChainError } from '@core/errors'
|
|
3089
3311
|
*
|
|
3090
|
-
* throw
|
|
3091
|
-
* // Message: "
|
|
3312
|
+
* throw createInvalidChainError('UnknownChain', 'Chain is not supported by this bridge')
|
|
3313
|
+
* // Message: "Invalid chain 'UnknownChain': Chain is not supported by this bridge"
|
|
3092
3314
|
* ```
|
|
3093
3315
|
*/
|
|
3094
|
-
function
|
|
3316
|
+
function createInvalidChainError(chain, reason) {
|
|
3095
3317
|
const errorDetails = {
|
|
3096
|
-
...InputError.
|
|
3318
|
+
...InputError.INVALID_CHAIN,
|
|
3097
3319
|
recoverability: 'FATAL',
|
|
3098
|
-
message: `
|
|
3320
|
+
message: `Invalid chain '${chain}': ${reason}`,
|
|
3099
3321
|
cause: {
|
|
3100
|
-
trace: {
|
|
3322
|
+
trace: { chain, reason },
|
|
3101
3323
|
},
|
|
3102
3324
|
};
|
|
3103
3325
|
return new KitError(errorDetails);
|
|
3104
3326
|
}
|
|
3105
3327
|
|
|
3328
|
+
/**
|
|
3329
|
+
* Creates error for insufficient token balance.
|
|
3330
|
+
*
|
|
3331
|
+
* This error is thrown when a wallet does not have enough tokens to
|
|
3332
|
+
* complete a transaction. The error is FATAL as it requires user
|
|
3333
|
+
* intervention to add funds.
|
|
3334
|
+
*
|
|
3335
|
+
* @param chain - The blockchain network where the balance check failed
|
|
3336
|
+
* @param token - The token symbol (e.g., 'USDC', 'ETH')
|
|
3337
|
+
* @param rawError - The original error from the underlying system (optional)
|
|
3338
|
+
* @returns KitError with insufficient token balance details
|
|
3339
|
+
*
|
|
3340
|
+
* @example
|
|
3341
|
+
* ```typescript
|
|
3342
|
+
* import { createInsufficientTokenBalanceError } from '@core/errors'
|
|
3343
|
+
*
|
|
3344
|
+
* throw createInsufficientTokenBalanceError('Ethereum', 'USDC')
|
|
3345
|
+
* // Message: "Insufficient USDC balance on Ethereum"
|
|
3346
|
+
* ```
|
|
3347
|
+
*
|
|
3348
|
+
* @example
|
|
3349
|
+
* ```typescript
|
|
3350
|
+
* // With raw error for debugging
|
|
3351
|
+
* try {
|
|
3352
|
+
* await transfer(...)
|
|
3353
|
+
* } catch (error) {
|
|
3354
|
+
* throw createInsufficientTokenBalanceError('Base', 'USDC', error)
|
|
3355
|
+
* }
|
|
3356
|
+
* ```
|
|
3357
|
+
*/
|
|
3358
|
+
function createInsufficientTokenBalanceError(chain, token, rawError) {
|
|
3359
|
+
return new KitError({
|
|
3360
|
+
...BalanceError.INSUFFICIENT_TOKEN,
|
|
3361
|
+
recoverability: 'FATAL',
|
|
3362
|
+
message: `Insufficient ${token} balance on ${chain}`,
|
|
3363
|
+
cause: {
|
|
3364
|
+
trace: {
|
|
3365
|
+
chain,
|
|
3366
|
+
token,
|
|
3367
|
+
rawError,
|
|
3368
|
+
},
|
|
3369
|
+
},
|
|
3370
|
+
});
|
|
3371
|
+
}
|
|
3372
|
+
/**
|
|
3373
|
+
* Creates error for insufficient gas funds.
|
|
3374
|
+
*
|
|
3375
|
+
* This error is thrown when a wallet does not have enough native tokens
|
|
3376
|
+
* (ETH, SOL, etc.) to pay for transaction gas fees. The error is FATAL
|
|
3377
|
+
* as it requires user intervention to add gas funds.
|
|
3378
|
+
*
|
|
3379
|
+
* @param chain - The blockchain network where the gas check failed
|
|
3380
|
+
* @param rawError - The original error from the underlying system (optional)
|
|
3381
|
+
* @returns KitError with insufficient gas details
|
|
3382
|
+
*
|
|
3383
|
+
* @example
|
|
3384
|
+
* ```typescript
|
|
3385
|
+
* import { createInsufficientGasError } from '@core/errors'
|
|
3386
|
+
*
|
|
3387
|
+
* throw createInsufficientGasError('Ethereum')
|
|
3388
|
+
* // Message: "Insufficient gas funds on Ethereum"
|
|
3389
|
+
* ```
|
|
3390
|
+
*/
|
|
3391
|
+
function createInsufficientGasError(chain, rawError) {
|
|
3392
|
+
return new KitError({
|
|
3393
|
+
...BalanceError.INSUFFICIENT_GAS,
|
|
3394
|
+
recoverability: 'FATAL',
|
|
3395
|
+
message: `Insufficient gas funds on ${chain}`,
|
|
3396
|
+
cause: {
|
|
3397
|
+
trace: {
|
|
3398
|
+
chain,
|
|
3399
|
+
rawError,
|
|
3400
|
+
},
|
|
3401
|
+
},
|
|
3402
|
+
});
|
|
3403
|
+
}
|
|
3404
|
+
|
|
3405
|
+
/**
|
|
3406
|
+
* Creates error for transaction simulation failures.
|
|
3407
|
+
*
|
|
3408
|
+
* This error is thrown when a pre-flight transaction simulation fails,
|
|
3409
|
+
* typically due to contract logic that would revert. The error is FATAL
|
|
3410
|
+
* as it indicates the transaction would fail if submitted.
|
|
3411
|
+
*
|
|
3412
|
+
* @param chain - The blockchain network where the simulation failed
|
|
3413
|
+
* @param reason - The reason for simulation failure (e.g., revert message)
|
|
3414
|
+
* @param rawError - The original error from the underlying system (optional)
|
|
3415
|
+
* @returns KitError with simulation failure details
|
|
3416
|
+
*
|
|
3417
|
+
* @example
|
|
3418
|
+
* ```typescript
|
|
3419
|
+
* import { createSimulationFailedError } from '@core/errors'
|
|
3420
|
+
*
|
|
3421
|
+
* throw createSimulationFailedError('Ethereum', 'ERC20: insufficient allowance')
|
|
3422
|
+
* // Message: "Simulation failed on Ethereum: ERC20: insufficient allowance"
|
|
3423
|
+
* ```
|
|
3424
|
+
*/
|
|
3425
|
+
function createSimulationFailedError(chain, reason, rawError) {
|
|
3426
|
+
return new KitError({
|
|
3427
|
+
...OnchainError.SIMULATION_FAILED,
|
|
3428
|
+
recoverability: 'FATAL',
|
|
3429
|
+
message: `Simulation failed on ${chain}: ${reason}`,
|
|
3430
|
+
cause: {
|
|
3431
|
+
trace: {
|
|
3432
|
+
chain,
|
|
3433
|
+
reason,
|
|
3434
|
+
rawError,
|
|
3435
|
+
},
|
|
3436
|
+
},
|
|
3437
|
+
});
|
|
3438
|
+
}
|
|
3439
|
+
/**
|
|
3440
|
+
* Creates error for transaction reverts.
|
|
3441
|
+
*
|
|
3442
|
+
* This error is thrown when a transaction is submitted and confirmed
|
|
3443
|
+
* but reverts on-chain. The error is FATAL as it indicates the
|
|
3444
|
+
* transaction executed but failed.
|
|
3445
|
+
*
|
|
3446
|
+
* @param chain - The blockchain network where the transaction reverted
|
|
3447
|
+
* @param reason - The reason for the revert (e.g., revert message)
|
|
3448
|
+
* @param rawError - The original error from the underlying system (optional)
|
|
3449
|
+
* @returns KitError with transaction revert details
|
|
3450
|
+
*
|
|
3451
|
+
* @example
|
|
3452
|
+
* ```typescript
|
|
3453
|
+
* import { createTransactionRevertedError } from '@core/errors'
|
|
3454
|
+
*
|
|
3455
|
+
* throw createTransactionRevertedError('Base', 'Slippage exceeded')
|
|
3456
|
+
* // Message: "Transaction reverted on Base: Slippage exceeded"
|
|
3457
|
+
* ```
|
|
3458
|
+
*/
|
|
3459
|
+
function createTransactionRevertedError(chain, reason, rawError) {
|
|
3460
|
+
return new KitError({
|
|
3461
|
+
...OnchainError.TRANSACTION_REVERTED,
|
|
3462
|
+
recoverability: 'FATAL',
|
|
3463
|
+
message: `Transaction reverted on ${chain}: ${reason}`,
|
|
3464
|
+
cause: {
|
|
3465
|
+
trace: {
|
|
3466
|
+
chain,
|
|
3467
|
+
reason,
|
|
3468
|
+
rawError,
|
|
3469
|
+
},
|
|
3470
|
+
},
|
|
3471
|
+
});
|
|
3472
|
+
}
|
|
3473
|
+
/**
|
|
3474
|
+
* Creates error for out of gas failures.
|
|
3475
|
+
*
|
|
3476
|
+
* This error is thrown when a transaction runs out of gas during execution.
|
|
3477
|
+
* The error is FATAL as it requires adjusting gas limits or transaction logic.
|
|
3478
|
+
*
|
|
3479
|
+
* @param chain - The blockchain network where the transaction ran out of gas
|
|
3480
|
+
* @param rawError - The original error from the underlying system (optional)
|
|
3481
|
+
* @returns KitError with out of gas details
|
|
3482
|
+
*
|
|
3483
|
+
* @example
|
|
3484
|
+
* ```typescript
|
|
3485
|
+
* import { createOutOfGasError } from '@core/errors'
|
|
3486
|
+
*
|
|
3487
|
+
* throw createOutOfGasError('Polygon')
|
|
3488
|
+
* // Message: "Transaction ran out of gas on Polygon"
|
|
3489
|
+
* ```
|
|
3490
|
+
*/
|
|
3491
|
+
function createOutOfGasError(chain, rawError) {
|
|
3492
|
+
return new KitError({
|
|
3493
|
+
...OnchainError.OUT_OF_GAS,
|
|
3494
|
+
recoverability: 'FATAL',
|
|
3495
|
+
message: `Transaction ran out of gas on ${chain}`,
|
|
3496
|
+
cause: {
|
|
3497
|
+
trace: {
|
|
3498
|
+
chain,
|
|
3499
|
+
rawError,
|
|
3500
|
+
},
|
|
3501
|
+
},
|
|
3502
|
+
});
|
|
3503
|
+
}
|
|
3504
|
+
|
|
3505
|
+
/**
|
|
3506
|
+
* Creates error for RPC endpoint failures.
|
|
3507
|
+
*
|
|
3508
|
+
* This error is thrown when an RPC provider endpoint fails, returns an error,
|
|
3509
|
+
* or is unavailable. The error is RETRYABLE as RPC issues are often temporary.
|
|
3510
|
+
*
|
|
3511
|
+
* @param chain - The blockchain network where the RPC error occurred
|
|
3512
|
+
* @param rawError - The original error from the underlying system (optional)
|
|
3513
|
+
* @returns KitError with RPC endpoint error details
|
|
3514
|
+
*
|
|
3515
|
+
* @example
|
|
3516
|
+
* ```typescript
|
|
3517
|
+
* import { createRpcEndpointError } from '@core/errors'
|
|
3518
|
+
*
|
|
3519
|
+
* throw createRpcEndpointError('Ethereum')
|
|
3520
|
+
* // Message: "RPC endpoint error on Ethereum"
|
|
3521
|
+
* ```
|
|
3522
|
+
*/
|
|
3523
|
+
function createRpcEndpointError(chain, rawError) {
|
|
3524
|
+
return new KitError({
|
|
3525
|
+
...RpcError.ENDPOINT_ERROR,
|
|
3526
|
+
recoverability: 'RETRYABLE',
|
|
3527
|
+
message: `RPC endpoint error on ${chain}`,
|
|
3528
|
+
cause: {
|
|
3529
|
+
trace: {
|
|
3530
|
+
chain,
|
|
3531
|
+
rawError,
|
|
3532
|
+
},
|
|
3533
|
+
},
|
|
3534
|
+
});
|
|
3535
|
+
}
|
|
3536
|
+
|
|
3537
|
+
/**
|
|
3538
|
+
* Creates error for network connection failures.
|
|
3539
|
+
*
|
|
3540
|
+
* This error is thrown when network connectivity issues prevent reaching
|
|
3541
|
+
* the blockchain network. The error is RETRYABLE as network issues are
|
|
3542
|
+
* often temporary.
|
|
3543
|
+
*
|
|
3544
|
+
* @param chain - The blockchain network where the connection failed
|
|
3545
|
+
* @param rawError - The original error from the underlying system (optional)
|
|
3546
|
+
* @returns KitError with network connection error details
|
|
3547
|
+
*
|
|
3548
|
+
* @example
|
|
3549
|
+
* ```typescript
|
|
3550
|
+
* import { createNetworkConnectionError } from '@core/errors'
|
|
3551
|
+
*
|
|
3552
|
+
* throw createNetworkConnectionError('Ethereum')
|
|
3553
|
+
* // Message: "Network connection failed for Ethereum"
|
|
3554
|
+
* ```
|
|
3555
|
+
*/
|
|
3556
|
+
function createNetworkConnectionError(chain, rawError) {
|
|
3557
|
+
return new KitError({
|
|
3558
|
+
...NetworkError.CONNECTION_FAILED,
|
|
3559
|
+
recoverability: 'RETRYABLE',
|
|
3560
|
+
message: `Network connection failed for ${chain}`,
|
|
3561
|
+
cause: {
|
|
3562
|
+
trace: {
|
|
3563
|
+
chain,
|
|
3564
|
+
rawError,
|
|
3565
|
+
},
|
|
3566
|
+
},
|
|
3567
|
+
});
|
|
3568
|
+
}
|
|
3569
|
+
|
|
3570
|
+
/**
|
|
3571
|
+
* Parses raw blockchain errors into structured KitError instances.
|
|
3572
|
+
*
|
|
3573
|
+
* This function uses pattern matching to identify common blockchain error
|
|
3574
|
+
* types and converts them into standardized KitError format. It handles
|
|
3575
|
+
* errors from viem, ethers, Solana web3.js, and other blockchain libraries.
|
|
3576
|
+
*
|
|
3577
|
+
* The parser recognizes 5 main error patterns:
|
|
3578
|
+
* 1. Insufficient balance errors
|
|
3579
|
+
* 2. Simulation/execution reverted errors
|
|
3580
|
+
* 3. Gas-related errors
|
|
3581
|
+
* 4. Network connectivity errors
|
|
3582
|
+
* 5. RPC provider errors
|
|
3583
|
+
*
|
|
3584
|
+
* @param error - The raw error from the blockchain library
|
|
3585
|
+
* @param context - Context information including chain and optional token
|
|
3586
|
+
* @returns A structured KitError instance
|
|
3587
|
+
*
|
|
3588
|
+
* @example
|
|
3589
|
+
* ```typescript
|
|
3590
|
+
* import { parseBlockchainError } from '@core/errors'
|
|
3591
|
+
*
|
|
3592
|
+
* try {
|
|
3593
|
+
* await walletClient.sendTransaction(...)
|
|
3594
|
+
* } catch (error) {
|
|
3595
|
+
* throw parseBlockchainError(error, {
|
|
3596
|
+
* chain: 'Ethereum',
|
|
3597
|
+
* token: 'USDC',
|
|
3598
|
+
* operation: 'transfer'
|
|
3599
|
+
* })
|
|
3600
|
+
* }
|
|
3601
|
+
* ```
|
|
3602
|
+
*
|
|
3603
|
+
* @example
|
|
3604
|
+
* ```typescript
|
|
3605
|
+
* // Minimal usage
|
|
3606
|
+
* try {
|
|
3607
|
+
* await connection.sendTransaction(...)
|
|
3608
|
+
* } catch (error) {
|
|
3609
|
+
* throw parseBlockchainError(error, { chain: 'Solana' })
|
|
3610
|
+
* }
|
|
3611
|
+
* ```
|
|
3612
|
+
*/
|
|
3613
|
+
function parseBlockchainError(error, context) {
|
|
3614
|
+
const msg = extractMessage(error);
|
|
3615
|
+
const token = context.token ?? 'token';
|
|
3616
|
+
// Pattern 1: Insufficient balance errors
|
|
3617
|
+
// Matches balance-related errors from ERC20 contracts, native transfers, and Solana programs
|
|
3618
|
+
if (/transfer amount exceeds balance|insufficient (balance|funds)|burn amount exceeded/i.test(msg)) {
|
|
3619
|
+
return createInsufficientTokenBalanceError(context.chain, token, error);
|
|
3620
|
+
}
|
|
3621
|
+
// Pattern 2: Simulation and execution reverts
|
|
3622
|
+
// Matches contract revert errors and simulation failures
|
|
3623
|
+
if (/execution reverted|simulation failed|transaction reverted|transaction failed/i.test(msg)) {
|
|
3624
|
+
const reason = extractRevertReason(msg) ?? 'Transaction reverted';
|
|
3625
|
+
// Distinguish between simulation failures and transaction reverts
|
|
3626
|
+
// "simulation failed" or "eth_call" indicates pre-flight simulation
|
|
3627
|
+
// "transaction failed" or context.operation === 'transaction' indicates post-execution
|
|
3628
|
+
if (/simulation failed/i.test(msg) || context.operation === 'simulation') {
|
|
3629
|
+
return createSimulationFailedError(context.chain, reason, error);
|
|
3630
|
+
}
|
|
3631
|
+
// Transaction execution failures or reverts
|
|
3632
|
+
return createTransactionRevertedError(context.chain, reason, error);
|
|
3633
|
+
}
|
|
3634
|
+
// Pattern 3: Gas-related errors
|
|
3635
|
+
// Matches gas estimation failures and gas exhaustion
|
|
3636
|
+
// Check specific patterns first, then generic "gas" patterns
|
|
3637
|
+
// Gas estimation failures are RPC issues
|
|
3638
|
+
if (/gas estimation failed|cannot estimate gas/i.test(msg)) {
|
|
3639
|
+
return createRpcEndpointError(context.chain, error);
|
|
3640
|
+
}
|
|
3641
|
+
// Gas exhaustion errors
|
|
3642
|
+
// Use specific patterns without wildcards to avoid ReDoS
|
|
3643
|
+
if (/out of gas|gas limit exceeded|exceeds block gas limit/i.test(msg)) {
|
|
3644
|
+
return createOutOfGasError(context.chain, error);
|
|
3645
|
+
}
|
|
3646
|
+
// Insufficient funds for gas
|
|
3647
|
+
if (/insufficient funds for gas/i.test(msg)) {
|
|
3648
|
+
return createInsufficientGasError(context.chain, error);
|
|
3649
|
+
}
|
|
3650
|
+
// Pattern 4: Network connectivity errors
|
|
3651
|
+
// Matches connection failures, DNS errors, and timeouts
|
|
3652
|
+
if (/connection (refused|failed)|network|timeout|ENOTFOUND|ECONNREFUSED/i.test(msg)) {
|
|
3653
|
+
return createNetworkConnectionError(context.chain, error);
|
|
3654
|
+
}
|
|
3655
|
+
// Pattern 5: RPC provider errors
|
|
3656
|
+
// Matches RPC endpoint errors, invalid responses, and rate limits
|
|
3657
|
+
if (/rpc|invalid response|rate limit|too many requests/i.test(msg)) {
|
|
3658
|
+
return createRpcEndpointError(context.chain, error);
|
|
3659
|
+
}
|
|
3660
|
+
// Fallback based on operation context
|
|
3661
|
+
// Gas-related operations are RPC calls
|
|
3662
|
+
if (context.operation === 'estimateGas' ||
|
|
3663
|
+
context.operation === 'getGasPrice') {
|
|
3664
|
+
return createRpcEndpointError(context.chain, error);
|
|
3665
|
+
}
|
|
3666
|
+
// Fallback for unrecognized errors
|
|
3667
|
+
// Defaults to simulation failed as transaction execution is the most common failure point
|
|
3668
|
+
return createSimulationFailedError(context.chain, msg.length > 0 ? msg : 'Unknown error', error);
|
|
3669
|
+
}
|
|
3670
|
+
/**
|
|
3671
|
+
* Type guard to check if error has Solana-Kit structure with logs.
|
|
3672
|
+
*
|
|
3673
|
+
* Checks if the error object contains a context with logs array,
|
|
3674
|
+
* which is the structure used by Solana Kit errors.
|
|
3675
|
+
*
|
|
3676
|
+
* @param error - Unknown error to check
|
|
3677
|
+
* @returns True if error has Solana-Kit logs structure
|
|
3678
|
+
*/
|
|
3679
|
+
function hasSolanaLogs(error) {
|
|
3680
|
+
return (error !== null &&
|
|
3681
|
+
typeof error === 'object' &&
|
|
3682
|
+
'context' in error &&
|
|
3683
|
+
error.context !== null &&
|
|
3684
|
+
typeof error.context === 'object' &&
|
|
3685
|
+
'logs' in error.context &&
|
|
3686
|
+
Array.isArray(error.context.logs));
|
|
3687
|
+
}
|
|
3688
|
+
/**
|
|
3689
|
+
* Extracts a human-readable error message from various error types.
|
|
3690
|
+
*
|
|
3691
|
+
* Handles Error objects, string errors, objects with message properties,
|
|
3692
|
+
* Solana-Kit errors with context logs, and falls back to string representation.
|
|
3693
|
+
* For Solana-Kit errors, extracts Anchor error messages from transaction logs.
|
|
3694
|
+
*
|
|
3695
|
+
* @param error - Unknown error to extract message from
|
|
3696
|
+
* @returns Extracted error message string
|
|
3697
|
+
*
|
|
3698
|
+
* @example
|
|
3699
|
+
* ```typescript
|
|
3700
|
+
* const msg1 = extractMessage(new Error('test')) // 'test'
|
|
3701
|
+
* const msg2 = extractMessage('string error') // 'string error'
|
|
3702
|
+
* const msg3 = extractMessage({ message: 'obj' }) // 'obj'
|
|
3703
|
+
* ```
|
|
3704
|
+
*/
|
|
3705
|
+
function extractMessage(error) {
|
|
3706
|
+
// Check for Solana-Kit errors with context.logs
|
|
3707
|
+
if (hasSolanaLogs(error)) {
|
|
3708
|
+
// Extract Anchor error message from logs
|
|
3709
|
+
const anchorLog = error.context.logs.find((log) => log.includes('AnchorError') || log.includes('Error Message'));
|
|
3710
|
+
if (anchorLog !== undefined) {
|
|
3711
|
+
// Return the anchor error log which contains the detailed message
|
|
3712
|
+
return anchorLog;
|
|
3713
|
+
}
|
|
3714
|
+
}
|
|
3715
|
+
if (error instanceof Error) {
|
|
3716
|
+
return error.message;
|
|
3717
|
+
}
|
|
3718
|
+
if (typeof error === 'string') {
|
|
3719
|
+
return error;
|
|
3720
|
+
}
|
|
3721
|
+
if (typeof error === 'object' && error !== null && 'message' in error) {
|
|
3722
|
+
return String(error.message);
|
|
3723
|
+
}
|
|
3724
|
+
return String(error);
|
|
3725
|
+
}
|
|
3726
|
+
/**
|
|
3727
|
+
* Extracts the revert reason from an error message.
|
|
3728
|
+
*
|
|
3729
|
+
* Attempts to parse out the meaningful reason from execution revert errors,
|
|
3730
|
+
* removing common prefixes like "execution reverted:" or "reverted:".
|
|
3731
|
+
*
|
|
3732
|
+
* @param msg - The error message to extract from
|
|
3733
|
+
* @returns The extracted revert reason, or null if not found
|
|
3734
|
+
*
|
|
3735
|
+
* @example
|
|
3736
|
+
* ```typescript
|
|
3737
|
+
* const reason = extractRevertReason(
|
|
3738
|
+
* 'execution reverted: ERC20: transfer amount exceeds balance'
|
|
3739
|
+
* )
|
|
3740
|
+
* // Returns: 'ERC20: transfer amount exceeds balance'
|
|
3741
|
+
* ```
|
|
3742
|
+
*
|
|
3743
|
+
* @example
|
|
3744
|
+
* ```typescript
|
|
3745
|
+
* const reason = extractRevertReason(
|
|
3746
|
+
* 'Simulation failed: Execution reverted with reason: Insufficient allowance'
|
|
3747
|
+
* )
|
|
3748
|
+
* // Returns: 'Insufficient allowance'
|
|
3749
|
+
* ```
|
|
3750
|
+
*/
|
|
3751
|
+
function extractRevertReason(msg) {
|
|
3752
|
+
// Try to extract reason after "execution reverted:" or "reason:"
|
|
3753
|
+
// Use [^\n.]+ instead of .+? to avoid ReDoS vulnerability
|
|
3754
|
+
const patterns = [
|
|
3755
|
+
/(?:execution reverted|reverted):\s*([^\n.]+)/i,
|
|
3756
|
+
/reason:\s*([^\n.]+)/i,
|
|
3757
|
+
/with reason:\s*([^\n.]+)/i,
|
|
3758
|
+
];
|
|
3759
|
+
for (const pattern of patterns) {
|
|
3760
|
+
const match = pattern.exec(msg);
|
|
3761
|
+
const extractedReason = match?.at(1);
|
|
3762
|
+
if (extractedReason !== undefined && extractedReason.length > 0) {
|
|
3763
|
+
return extractedReason.trim();
|
|
3764
|
+
}
|
|
3765
|
+
}
|
|
3766
|
+
return null;
|
|
3767
|
+
}
|
|
3768
|
+
|
|
3106
3769
|
/**
|
|
3107
3770
|
* Abstract class defining the standard interface for an adapter that interacts with a specific blockchain.
|
|
3108
3771
|
*
|
|
@@ -3248,20 +3911,24 @@ class Adapter {
|
|
|
3248
3911
|
* Validate that the target chain is supported by this adapter.
|
|
3249
3912
|
*
|
|
3250
3913
|
* @param targetChain - The chain to validate.
|
|
3251
|
-
* @throws
|
|
3914
|
+
* @throws KitError with INVALID_CHAIN code if the chain is not supported by this adapter.
|
|
3252
3915
|
*/
|
|
3253
3916
|
validateChainSupport(targetChain) {
|
|
3254
3917
|
if (this.capabilities?.supportedChains) {
|
|
3255
3918
|
const isSupported = this.capabilities.supportedChains.some((supportedChain) => supportedChain.chain === targetChain.chain);
|
|
3256
3919
|
if (!isSupported) {
|
|
3257
3920
|
const supportedCount = this.capabilities.supportedChains.length;
|
|
3258
|
-
|
|
3259
|
-
|
|
3921
|
+
// List supported chain names for better user experience
|
|
3922
|
+
const supportedChainNames = this.capabilities.supportedChains
|
|
3923
|
+
.map((chainDef) => chainDef.name)
|
|
3924
|
+
.join(', ');
|
|
3925
|
+
const reason = `Not supported by this adapter. It supports ${supportedCount.toString()} ${supportedCount === 1 ? 'chain' : 'chains'}: ${supportedChainNames}`;
|
|
3926
|
+
throw createInvalidChainError(targetChain.name, reason);
|
|
3260
3927
|
}
|
|
3261
3928
|
}
|
|
3262
3929
|
else if (this.chainType && targetChain.type !== this.chainType) {
|
|
3263
|
-
const
|
|
3264
|
-
throw
|
|
3930
|
+
const reason = `Chain type mismatch: adapter supports ${this.chainType} chains, but received ${targetChain.type ?? 'unknown'} chain`;
|
|
3931
|
+
throw createInvalidChainError(targetChain.name, reason);
|
|
3265
3932
|
}
|
|
3266
3933
|
}
|
|
3267
3934
|
}
|
|
@@ -9957,14 +10624,17 @@ class EthersAdapter extends EvmAdapter {
|
|
|
9957
10624
|
/**
|
|
9958
10625
|
* Simulates a contract function call using Ethers v6 `.staticCall`.
|
|
9959
10626
|
*/
|
|
9960
|
-
async simulateFunctionCall(contract, functionName, args) {
|
|
10627
|
+
async simulateFunctionCall(contract, functionName, args, chain) {
|
|
9961
10628
|
try {
|
|
9962
10629
|
const func = contract.getFunction(functionName);
|
|
9963
10630
|
await func.staticCall(...args);
|
|
9964
10631
|
}
|
|
9965
10632
|
catch (err) {
|
|
9966
|
-
|
|
9967
|
-
throw
|
|
10633
|
+
// Wrap simulation errors with structured error format
|
|
10634
|
+
throw parseBlockchainError(err, {
|
|
10635
|
+
chain: chain.name,
|
|
10636
|
+
operation: 'simulation',
|
|
10637
|
+
});
|
|
9968
10638
|
}
|
|
9969
10639
|
}
|
|
9970
10640
|
/**
|
|
@@ -9991,7 +10661,11 @@ class EthersAdapter extends EvmAdapter {
|
|
|
9991
10661
|
errorMessage.toLocaleLowerCase().includes('execution reverted')) {
|
|
9992
10662
|
return fallback;
|
|
9993
10663
|
}
|
|
9994
|
-
|
|
10664
|
+
// Wrap gas estimation errors with structured error format
|
|
10665
|
+
throw parseBlockchainError(error, {
|
|
10666
|
+
chain: chain.name,
|
|
10667
|
+
operation: 'estimateGas',
|
|
10668
|
+
});
|
|
9995
10669
|
}
|
|
9996
10670
|
let gasPrice;
|
|
9997
10671
|
try {
|
|
@@ -9999,7 +10673,11 @@ class EthersAdapter extends EvmAdapter {
|
|
|
9999
10673
|
gasPrice = await this.fetchGasPrice(chain);
|
|
10000
10674
|
}
|
|
10001
10675
|
catch (error) {
|
|
10002
|
-
|
|
10676
|
+
// Wrap gas price errors with structured error format
|
|
10677
|
+
throw parseBlockchainError(error, {
|
|
10678
|
+
chain: chain.name,
|
|
10679
|
+
operation: 'getGasPrice',
|
|
10680
|
+
});
|
|
10003
10681
|
}
|
|
10004
10682
|
return { gas, gasPrice, fee: (gas * gasPrice).toString() };
|
|
10005
10683
|
}
|
|
@@ -10055,7 +10733,11 @@ class EthersAdapter extends EvmAdapter {
|
|
|
10055
10733
|
const shouldRetry = ethersNonceManager.shouldRetryNonceError(error, userProvidedNonce);
|
|
10056
10734
|
// Don't retry if: not a nonce error, user provided nonce, or this is the last attempt
|
|
10057
10735
|
if (!shouldRetry || attempt === maxAttempts) {
|
|
10058
|
-
|
|
10736
|
+
// Wrap transaction execution errors with structured error format
|
|
10737
|
+
throw parseBlockchainError(error, {
|
|
10738
|
+
chain: chain.name,
|
|
10739
|
+
operation: 'sendTransaction',
|
|
10740
|
+
});
|
|
10059
10741
|
}
|
|
10060
10742
|
// Retry with resync on nonce-related errors (this will set a new nonce)
|
|
10061
10743
|
txRequest.nonce = await ethersNonceManager.resyncAndAllocate(provider, chain.chainId, fromAddress);
|
|
@@ -10228,7 +10910,7 @@ class EthersAdapter extends EvmAdapter {
|
|
|
10228
10910
|
},
|
|
10229
10911
|
execute: async (overrides) => {
|
|
10230
10912
|
// Simulate the function call to catch errors before submission
|
|
10231
|
-
await this.simulateFunctionCall(contract, functionName, args);
|
|
10913
|
+
await this.simulateFunctionCall(contract, functionName, args, targetChain);
|
|
10232
10914
|
await this.ensureChain(targetChain);
|
|
10233
10915
|
// Reconnect the contract with the current signer, which is on the correct
|
|
10234
10916
|
// chain after `ensureChain`, to ensure the transaction is populated and
|
|
@@ -10257,7 +10939,7 @@ class EthersAdapter extends EvmAdapter {
|
|
|
10257
10939
|
* adapter interface, it is effectively required and enforced at runtime. This ensures
|
|
10258
10940
|
* the adapter is connected to the correct chain before querying the signer.
|
|
10259
10941
|
*
|
|
10260
|
-
* @param chain - The chain to use for address resolution
|
|
10942
|
+
* @param chain - The chain to use for address resolution.
|
|
10261
10943
|
* @returns A promise that resolves to the signer's address
|
|
10262
10944
|
* @throws Error when no chain is provided
|
|
10263
10945
|
*
|
|
@@ -10269,26 +10951,15 @@ class EthersAdapter extends EvmAdapter {
|
|
|
10269
10951
|
* const address = await adapter.getAddress(Ethereum)
|
|
10270
10952
|
* console.log('Wallet address:', address)
|
|
10271
10953
|
* ```
|
|
10272
|
-
*
|
|
10273
|
-
* @example
|
|
10274
|
-
* ```typescript
|
|
10275
|
-
* // In practice, address resolution happens automatically
|
|
10276
|
-
* const prepared = await adapter.prepare(params, {
|
|
10277
|
-
* chain: 'Ethereum'
|
|
10278
|
-
* // Address automatically resolved via getAddress() internally
|
|
10279
|
-
* })
|
|
10280
|
-
* ```
|
|
10281
10954
|
*/
|
|
10282
10955
|
async getAddress(chain) {
|
|
10283
10956
|
// Prevent calling getAddress on developer-controlled adapters
|
|
10284
10957
|
if (this.capabilities?.addressContext === 'developer-controlled') {
|
|
10285
|
-
throw new Error('Cannot call getAddress() on developer-controlled adapters. '
|
|
10286
|
-
'Address must be provided explicitly in the operation context.');
|
|
10958
|
+
throw new Error('Cannot call getAddress() on developer-controlled adapters. Address must be provided explicitly in the operation context.');
|
|
10287
10959
|
}
|
|
10288
10960
|
// Chain parameter should now be provided by resolveOperationContext
|
|
10289
10961
|
if (!chain) {
|
|
10290
|
-
throw new Error('Chain parameter is required for address resolution. '
|
|
10291
|
-
'This should be provided by the OperationContext pattern.');
|
|
10962
|
+
throw new Error('Chain parameter is required for address resolution. This should be provided by the OperationContext pattern.');
|
|
10292
10963
|
}
|
|
10293
10964
|
// Ensure we're on the correct chain before getting address
|
|
10294
10965
|
await this.ensureChain(chain);
|