Skip to content

Commit b581451

Browse files
committed
refactor: error handlers
1 parent 5bd53df commit b581451

File tree

5 files changed

+84
-52
lines changed

5 files changed

+84
-52
lines changed

src/common/enums.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
export enum ErrorType {
2-
UserRejectError = 'UserRejectError',
32
EmptyError = 'EmptyError',
43
RevertError = 'RevertError',
54
PanicError = 'PanicError',
6-
UnknownError = 'UnknownError',
75
CustomError = 'CustomError',
6+
UserRejectError = 'UserRejectError',
87
RpcError = 'RpcError',
8+
UnknownError = 'UnknownError',
99
}

src/error-decoder.ts

Lines changed: 24 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -6,26 +6,26 @@ import {
66
ErrorHandler,
77
PanicErrorHandler,
88
RevertErrorHandler,
9+
RpcErrorHandler,
10+
UserRejectionHandler,
911
} from './errors/handlers'
10-
import { rpcErrorResult, unknownErrorResult, userRejectErrorResult } from './errors/results'
12+
import { unknownErrorResult } from './errors/results'
1113

1214
export class ErrorDecoder {
13-
private readonly errorHandlers: {
14-
predicate: (data: string) => boolean
15-
handler: ErrorHandler['handle']
16-
}[] = []
15+
private readonly errorHandlers: ErrorHandler[] = []
1716

1817
private constructor(
1918
handlers: ErrorHandler[],
2019
public readonly errorInterface: Interface | undefined,
2120
) {
2221
this.errorHandlers = handlers.map((handler) => ({
2322
predicate: handler.predicate,
24-
handler: handler.handle,
23+
handle: handler.handle,
2524
}))
2625
}
2726

2827
private async getContractOrTransactionError(error: Error): Promise<Error> {
28+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
2929
const errorReceipt = (error as any).receipt as TransactionReceipt
3030

3131
if (!errorReceipt) return error
@@ -56,12 +56,12 @@ export class ErrorDecoder {
5656
}
5757
}
5858

59-
private getDataFromError(error: Error): string {
59+
private getDataFromError(error: Error): string | undefined {
6060
// eslint-disable-next-line @typescript-eslint/no-explicit-any
6161
const errorData = (error as any).data ?? (error as any).error?.data
6262

6363
if (errorData === undefined) {
64-
throw error
64+
return undefined
6565
}
6666

6767
let returnData = typeof errorData === 'string' ? errorData : errorData.data
@@ -71,7 +71,7 @@ export class ErrorDecoder {
7171
}
7272

7373
if (returnData === undefined || typeof returnData !== 'string') {
74-
throw error
74+
return undefined
7575
}
7676

7777
return returnData
@@ -82,39 +82,25 @@ export class ErrorDecoder {
8282
return unknownErrorResult({
8383
data: undefined,
8484
// eslint-disable-next-line @typescript-eslint/no-explicit-any
85-
reason: (error as any).message ?? 'Unexpected error',
85+
reason: (error as any).message ?? 'Invalid error',
8686
})
8787
}
8888

89-
try {
90-
const targetError = await this.getContractOrTransactionError(error)
91-
const returnData = this.getDataFromError(targetError)
89+
const targetError = await this.getContractOrTransactionError(error)
90+
const returnData = this.getDataFromError(targetError)
9291

93-
for (const { predicate, handler } of this.errorHandlers) {
94-
if (predicate(returnData)) {
95-
return handler(returnData, this.errorInterface)
96-
}
92+
for (const { predicate, handle } of this.errorHandlers) {
93+
if (predicate(returnData, targetError)) {
94+
return handle(returnData, { errorInterface: this.errorInterface, error: targetError })
9795
}
98-
99-
return unknownErrorResult({
100-
data: returnData,
101-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
102-
reason: (error as any).message ?? 'Unrecognised error',
103-
})
104-
} catch (e) {
105-
if (error.message) {
106-
if (error.message.includes('rejected transaction')) {
107-
return userRejectErrorResult({ data: null, reason: error.message })
108-
}
109-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
110-
const rpcError = error as any
111-
if (rpcError.code !== undefined) {
112-
return rpcErrorResult({ data: null, name: rpcError.code, reason: error.message })
113-
}
114-
return unknownErrorResult({ data: null, reason: error.message })
115-
}
116-
return unknownErrorResult({ data: null })
11796
}
97+
98+
return unknownErrorResult({
99+
data: returnData,
100+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
101+
reason: (targetError as any)?.message ?? 'Unexpected error',
102+
name: targetError?.name,
103+
})
118104
}
119105

120106
public static create(
@@ -142,6 +128,8 @@ export class ErrorDecoder {
142128
new RevertErrorHandler(),
143129
new PanicErrorHandler(),
144130
new CustomErrorHandler(),
131+
new UserRejectionHandler(),
132+
new RpcErrorHandler(),
145133
...(additionalErrorHandlers ?? []),
146134
]
147135
return new ErrorDecoder(handlers, errorInterface)

src/errors/handlers.ts

Lines changed: 44 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,16 @@ import {
77
emptyErrorResult,
88
panicErrorResult,
99
revertErrorResult,
10+
rpcErrorResult,
1011
unknownErrorResult,
12+
userRejectErrorResult,
1113
} from './results'
1214

15+
type ErrorHandlerErrorInfo = { errorInterface: Interface; error: Error }
16+
1317
export interface ErrorHandler {
14-
predicate: (data: string) => boolean
15-
handle: (data: string, errorInterface?: Interface) => DecodedError
18+
predicate: (data: string | undefined, error: Error) => boolean
19+
handle: (data: string | undefined, errorInfo: ErrorHandlerErrorInfo) => DecodedError
1620
}
1721

1822
export class EmptyErrorHandler implements ErrorHandler {
@@ -27,7 +31,7 @@ export class EmptyErrorHandler implements ErrorHandler {
2731

2832
export class RevertErrorHandler implements ErrorHandler {
2933
public predicate(data: string): boolean {
30-
return data.startsWith(ERROR_STRING_PREFIX)
34+
return data?.startsWith(ERROR_STRING_PREFIX)
3135
}
3236

3337
public handle(data: string): DecodedError {
@@ -46,7 +50,7 @@ export class RevertErrorHandler implements ErrorHandler {
4650

4751
export class PanicErrorHandler implements ErrorHandler {
4852
public predicate(data: string): boolean {
49-
return data.startsWith(PANIC_CODE_PREFIX)
53+
return data?.startsWith(PANIC_CODE_PREFIX)
5054
}
5155

5256
public handle(data: string): DecodedError {
@@ -66,11 +70,14 @@ export class PanicErrorHandler implements ErrorHandler {
6670
export class CustomErrorHandler implements ErrorHandler {
6771
public predicate(data: string): boolean {
6872
return (
69-
data !== '0x' && !data.startsWith(ERROR_STRING_PREFIX) && !data.startsWith(PANIC_CODE_PREFIX)
73+
data &&
74+
data !== '0x' &&
75+
!data?.startsWith(ERROR_STRING_PREFIX) &&
76+
!data?.startsWith(PANIC_CODE_PREFIX)
7077
)
7178
}
7279

73-
public handle(data: string, errorInterface?: Interface): DecodedError {
80+
public handle(data: string, { errorInterface }: ErrorHandlerErrorInfo): DecodedError {
7481
let result: Parameters<typeof customErrorResult>[0] = { data }
7582
if (errorInterface) {
7683
const customError = errorInterface.parseError(data)
@@ -84,3 +91,34 @@ export class CustomErrorHandler implements ErrorHandler {
8491
return customErrorResult(result)
8592
}
8693
}
94+
95+
export class UserRejectionHandler implements ErrorHandler {
96+
public predicate(data: string, error: Error): boolean {
97+
return !data && error?.message?.includes('rejected transaction')
98+
}
99+
100+
public handle(_data: string, { error }: ErrorHandlerErrorInfo): DecodedError {
101+
return userRejectErrorResult({
102+
data: null,
103+
reason: error.message ?? 'The transaction was rejected',
104+
})
105+
}
106+
}
107+
108+
export class RpcErrorHandler implements ErrorHandler {
109+
public predicate(data: string, error: Error): boolean {
110+
return (
111+
!data &&
112+
error.message &&
113+
!error?.message?.includes('rejected transaction') &&
114+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
115+
(error as any).code !== undefined
116+
)
117+
}
118+
119+
public handle(_data: string, { error }: ErrorHandlerErrorInfo): DecodedError {
120+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
121+
const rpcError = error as any
122+
return rpcErrorResult({ data: null, name: rpcError.code, reason: rpcError.message })
123+
}
124+
}

src/errors/results.ts

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,17 @@ type ErrorResultFormatterParam = {
1313

1414
type ErrorResultFormatter = (params: ErrorResultFormatterParam) => DecodedError
1515

16+
const formatReason = (
17+
reason: string | undefined | null,
18+
defaultReason: string | null,
19+
): string | null => (reason && reason.trim() !== '' ? reason : defaultReason)
20+
1621
const baseErrorResult: (
1722
params: ErrorResultFormatterParam & { type: ErrorType },
1823
) => DecodedError = ({ type, data, reason, fragment, args, selector, name }) => {
1924
let res: DecodedError = {
2025
type,
21-
reason: reason ?? null,
26+
reason: formatReason(reason, null),
2227
data: data ?? null,
2328
fragment: null,
2429
args: args ?? new Result(),
@@ -45,7 +50,7 @@ export const emptyErrorResult: ErrorResultFormatter = ({ data }) =>
4550
export const userRejectErrorResult: ErrorResultFormatter = ({ data = null, reason }) =>
4651
baseErrorResult({
4752
type: ErrorType.UserRejectError,
48-
reason: reason ?? 'User has rejected the transaction',
53+
reason: formatReason(reason, 'User has rejected the transaction'),
4954
data,
5055
})
5156

@@ -59,11 +64,12 @@ export const revertErrorResult: ErrorResultFormatter = ({ data, reason, fragment
5964
})
6065
}
6166

62-
export const unknownErrorResult: ErrorResultFormatter = ({ data, reason }) => {
67+
export const unknownErrorResult: ErrorResultFormatter = ({ data, reason, name }) => {
6368
return baseErrorResult({
6469
type: ErrorType.UnknownError,
65-
reason: reason ?? 'Unknown error',
70+
reason: formatReason(reason, 'Unknown error'),
6671
data,
72+
name
6773
})
6874
}
6975

@@ -79,7 +85,7 @@ export const customErrorResult: ErrorResultFormatter = ({ data, reason, fragment
7985
const selector = data.slice(0, 10)
8086
return baseErrorResult({
8187
type: ErrorType.CustomError,
82-
reason: reason ?? `No ABI for custom error ${selector}`,
88+
reason: formatReason(reason, `No ABI for custom error ${selector}`),
8389
data,
8490
fragment,
8591
args,
@@ -91,7 +97,7 @@ export const customErrorResult: ErrorResultFormatter = ({ data, reason, fragment
9197
export const rpcErrorResult: ErrorResultFormatter = ({ reason, name }) =>
9298
baseErrorResult({
9399
type: ErrorType.RpcError,
94-
reason: reason ?? 'Error from JSON RPC provider',
100+
reason: formatReason(reason, 'Error from JSON RPC provider'),
95101
data: null,
96102
name: name?.toString() ?? null,
97103
})

test/index.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,8 @@ describe('ErrorDecoder', () => {
4646
expect(decodedError.reason).to.equal(fakeErrorMsg)
4747
})
4848

49-
it('should return error message as Unexpected error', async () => {
50-
expect(decodedError.reason).to.equal('Unexpected error')
49+
it('should return error message as Invalid error', async () => {
50+
expect(decodedError.reason).to.equal('Invalid error')
5151
})
5252

5353
it('should return null data', async () => {

0 commit comments

Comments
 (0)