-
Notifications
You must be signed in to change notification settings - Fork 13
Expand file tree
/
Copy patherror-decoder.ts
More file actions
137 lines (118 loc) · 4.08 KB
/
error-decoder.ts
File metadata and controls
137 lines (118 loc) · 4.08 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
import { ErrorFragment, Fragment, Interface, JsonFragment, TransactionReceipt } from 'ethers'
import { DecodedError } from './types'
import {
CustomErrorHandler,
EmptyErrorHandler,
ErrorHandler,
PanicErrorHandler,
RevertErrorHandler,
RpcErrorHandler,
UserRejectionHandler,
} from './errors/handlers'
import { unknownErrorResult } from './errors/results'
export class ErrorDecoder {
private readonly errorHandlers: ErrorHandler[] = []
private constructor(
handlers: ErrorHandler[],
public readonly errorInterface: Interface | undefined,
) {
this.errorHandlers = handlers.map((handler) => ({
predicate: handler.predicate,
handle: handler.handle,
}))
}
private async getContractOrTransactionError(error: Error): Promise<Error> {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const errorReceipt = (error as any).receipt as TransactionReceipt
if (!errorReceipt) return error
const resError = await this.getTransactionError(errorReceipt)
if (!resError) return error
return resError
}
private async getTransactionError(errorReceipt: TransactionReceipt): Promise<Error | null> {
if (!errorReceipt || errorReceipt.status !== 0) {
return undefined
}
const txHash = errorReceipt.hash
const provider = errorReceipt.provider
const tx = await provider.getTransaction(txHash)
try {
await provider.call({
...tx,
maxFeePerGas: undefined,
maxPriorityFeePerGas: undefined,
})
return null
} catch (e) {
return e as Error
}
}
private getDataFromError(error: Error): string | undefined {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const errorData = (error as any).data ?? (error as any).error?.data
if (errorData === undefined || errorData === null) {
return undefined
}
let returnData = typeof errorData === 'string' ? errorData : errorData.data
if (typeof returnData === 'object' && returnData.data) {
returnData = returnData.data
}
if (returnData === undefined || typeof returnData !== 'string') {
return undefined
}
return returnData
}
public async decode(error: unknown | Error): Promise<DecodedError> {
if (!(error instanceof Error)) {
return unknownErrorResult({
data: undefined,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
reason: (error as any).message ?? 'Invalid error',
})
}
const targetError = await this.getContractOrTransactionError(error)
const returnData = this.getDataFromError(targetError)
for (const { predicate, handle } of this.errorHandlers) {
if (predicate(returnData, targetError)) {
return handle(returnData, { errorInterface: this.errorInterface, error: targetError })
}
}
return unknownErrorResult({
data: returnData,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
reason: (targetError as any)?.message ?? 'Unexpected error',
name: targetError?.name,
})
}
public static create(
errorInterfaces?: ReadonlyArray<Fragment[] | JsonFragment[] | Interface>,
opts: {
additionalErrorHandlers?: ErrorHandler[]
} = {},
): ErrorDecoder {
const { additionalErrorHandlers } = opts
let errorInterface: Interface | undefined
if (errorInterfaces) {
const errorFragments = errorInterfaces.flatMap((iface) => {
if (iface instanceof Interface) {
return iface.fragments.filter((fragment) => ErrorFragment.isFragment(fragment))
} else {
return (iface as Fragment[]).filter(
(fragment) => fragment.type === 'error' || ErrorFragment.isFragment(fragment),
)
}
})
errorInterface = new Interface(errorFragments)
}
const handlers = [
new EmptyErrorHandler(),
new RevertErrorHandler(),
new PanicErrorHandler(),
new CustomErrorHandler(),
new UserRejectionHandler(),
new RpcErrorHandler(),
...(additionalErrorHandlers ?? []),
]
return new ErrorDecoder(handlers, errorInterface)
}
}