-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy patherror.ts
169 lines (158 loc) · 6.38 KB
/
error.ts
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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
import * as Sentry from '@sentry/nextjs'
// Passing a map will allow common errors to be mapped to a custom error message
// for the given context.
export const processError = (
error: Error | any,
{
tags,
extra,
transform,
overrideCapture,
forceCapture,
}: {
tags?: Record<string, string | number | boolean | null | undefined>
extra?: Record<string, unknown>
transform?: Partial<Record<CommonError, string>>
overrideCapture?: Partial<Record<CommonError, boolean>>
// If set to true, will capture error. If set to false, will not capture
// error. If undefined, will use capture map.
forceCapture?: boolean
} = {}
): string => {
// Convert to error type.
if (!(error instanceof Error)) {
error = new Error(`${error}`)
}
const { message } = error as Error
let recognizedError
// Attempt to recognize error.
for (const [commonError, patterns] of commonErrorPatternsEntries) {
// Match if any elements are matches.
const match = patterns.some((pattern) =>
Array.isArray(pattern)
? // If array of strings, every element must match.
pattern.every((p) => message.includes(p))
: message.includes(pattern)
)
// If recognized error, break.
if (match) {
recognizedError = commonError
break
}
}
// If recognized error, try to find it in the map, or else return the
// recognized error.
if (recognizedError) {
// Send to Sentry if we want to capture this recognized error.
if (
forceCapture !== false &&
((forceCapture === true ||
(overrideCapture && overrideCapture[recognizedError])) ??
captureCommonErrorMap[recognizedError])
) {
Sentry.captureException(error, { extra, tags })
}
return ((transform && transform[recognizedError]) ||
recognizedError) as string
}
// If we did not recognize the error and it's a Cosmos SDK error with a
// stacktrace, extract the error from the last line (since the first n-1 lines
// are golang stacktrace). This is a common string displayed in Cosmos SDK
// stacktraces.
if (
message.includes('github.com/cosmos/cosmos-sdk/baseapp.gRPCErrorToSDKError')
) {
error = new Error(message.split('\n').slice(-1)[0])
}
if (forceCapture !== false) {
// Send to Sentry since we were not expecting it.
Sentry.captureException(error, { extra, tags })
}
return error.message
}
// To add a new error:
// 1. Add a value to this enum.
// 2. Add matching parameters in commonErrorPatterns below.
// 3. If it should be sent to Sentry, add an entry to captureCommonErrorMap.
export enum CommonError {
RequestRejected = 'Wallet rejected transaction.',
InvalidAddress = 'Invalid address.',
InsufficientFees = "Insufficient fees. Reconnect your wallet, ensure you're on the right chain, and try again.",
InsufficientFunds = 'Insufficient funds.',
GetClientFailed = 'Failed to get client. Try refreshing the page or reconnecting your wallet.',
Network = 'Network error. Ensure you are connected to the internet, refresh the page, or try again later. If your network is working, the blockchain nodes may be having problems.',
Unauthorized = 'Unauthorized.',
InsufficientForProposalDeposit = 'Insufficient unstaked deposit tokens. Ensure you have enough unstaked deposit tokens to pay for the proposal deposit.',
PendingTransaction = 'You have another pending transaction. Please try again in 30 seconds.',
TextEncodingDecodingError = 'Text encoding/decoding error. Invalid character present in text.',
TxnSentTimeout = 'Transaction sent but has not yet been detected. Refresh this page to view its changes or check back later.',
InvalidJSONResponse = 'Invalid JSON response from server.',
NodeFailure = 'The blockchain nodes seem to be having problems. Try again later.',
BlockHeightTooLow = 'Block height is too low.',
TxPageOutOfRange = 'Transaction page is out of range.',
AuthorizationNotFound = 'Authorization does not exist.',
}
// List of error substrings to match to determine the common error. Elements in
// value are OR'd together. Inner string arrays are AND'd together. For example:
// ["abc", "def"] matches "abc" or "def" or "abc def". ["abc", ["def", "ghi"]]
// matches "abc def ghi" or "def ghi" but NOT "abc def" or "abc ghi".
const commonErrorPatterns: Record<CommonError, (string | string[])[]> = {
[CommonError.RequestRejected]: ['Request rejected'],
[CommonError.InvalidAddress]: [
'decoding bech32 failed: invalid checksum',
'contract: not found',
// Provided non-DAO address where a DAO address was expected.
'unknown variant `get_config`',
],
[CommonError.InsufficientFees]: ['insufficient fees'],
[CommonError.InsufficientFunds]: [
'insufficient funds',
// Try to send money with no balance.
'Account does not exist on chain.',
['fee payer address', 'does not exist'],
],
[CommonError.GetClientFailed]: [
'Bad status on response: 403',
'Failed to retrieve account from signer',
],
[CommonError.Network]: [
'Failed to fetch',
'socket disconnected',
'socket hang up',
'Bad status on response: 5',
'ECONNREFUSED',
'ETIMEDOUT',
'panic: invalid request',
'tx already exists in cache',
'Load failed',
],
[CommonError.Unauthorized]: ['Unauthorized'],
[CommonError.InsufficientForProposalDeposit]: ['Overflow: Cannot Sub with'],
[CommonError.PendingTransaction]: ['account sequence mismatch'],
[CommonError.TextEncodingDecodingError]: ['out of printable ASCII range'],
[CommonError.TxnSentTimeout]: [
'was submitted but was not yet found on the chain',
],
[CommonError.InvalidJSONResponse]: [
'invalid json response body',
'Unexpected token < in JSON',
],
[CommonError.NodeFailure]: ['goroutine'],
[CommonError.BlockHeightTooLow]: [
['32603', 'not available', 'lowest height is'],
],
[CommonError.TxPageOutOfRange]: [
['32603', 'page should be within', 'range', 'given'],
],
[CommonError.AuthorizationNotFound]: ['authorization not found'],
}
const commonErrorPatternsEntries = Object.entries(commonErrorPatterns) as [
CommonError,
(string | string[])[]
][]
// Whether or not to send the error to Sentry. Some errors we want to clean up
// for the user but still investigate (e.g. InvalidJSONResponse), so let's send
// them to Sentry even if we recognize them.
const captureCommonErrorMap: Partial<Record<CommonError, boolean>> = {
[CommonError.InvalidJSONResponse]: true,
}