forked from akure/dao-dao-ui
-
Notifications
You must be signed in to change notification settings - Fork 0
/
error.ts
202 lines (191 loc) · 7.81 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
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
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 sent error to Sentry. If set to false, will not send
* error to Sentry. If undefined, will use default behavior (reference the
* capture map).
*/
forceCapture?: boolean
} = {}
): string => {
// Convert to error type.
if (!(error instanceof Error)) {
error = new Error(
`${
// Some errors are not Error classes but have a message property.
typeof error === 'object' && 'message' in error ? error.message : 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 10 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.',
BlockHeightTooLow = 'Block height is too low.',
TxPageOutOfRange = 'Transaction page is out of range.',
AuthorizationNotFound = 'Authorization does not exist.',
SignatureVerificationFailedLedger = 'Signature verification failed. If you are using a Ledger, this is likely due to some unsupported symbols, such as "&", "<", or ">". Remove these symbols from the proposal title or description and try again.',
IbcClientExpired = 'IBC client expired. Reach out to us for help.',
IndexerDisabled = 'Indexer disabled.',
DaoInactive = 'This DAO is inactive, which means insufficient voting power has been staked. You cannot create a proposal at this time.',
ReconnectWallet = 'Please disconnect and reconnect your wallet.',
}
// 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', 'Ledger init aborted'],
[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.
'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',
'tx already exists in cache',
'Load failed',
'fetch 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.BlockHeightTooLow]: [
['32603', 'not available', 'lowest height is'],
],
[CommonError.TxPageOutOfRange]: [
['32603', 'page should be within', 'range', 'given'],
],
[CommonError.AuthorizationNotFound]: ['authorization not found'],
[CommonError.SignatureVerificationFailedLedger]: [
[
'Broadcasting transaction failed',
'signature verification failed; please verify account number',
'unauthorized',
],
],
[CommonError.IbcClientExpired]: [
[
'failed to send packet: cannot send packet using client',
'Expired: client is not active',
],
],
[CommonError.IndexerDisabled]: ['Indexer disabled.'],
[CommonError.DaoInactive]: [
'the DAO is currently inactive, you cannot create proposals',
],
[CommonError.ReconnectWallet]: [['Session', 'not established yet']],
}
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,
// Send to Sentry so we can tell how much this is happening.
[CommonError.SignatureVerificationFailedLedger]: true,
// This should be reported to us.
[CommonError.IbcClientExpired]: true,
}