@meshconnect/uwc-wallet-connect-connector 0.2.12 → 0.2.13
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.
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Parse an error and throw a WalletConnectorError
|
|
3
|
-
* Detects if the error is a user rejection or
|
|
3
|
+
* Detects if the error is a user rejection, expired proposal, or unknown error
|
|
4
4
|
*/
|
|
5
5
|
export declare function parseError(error: unknown): never;
|
|
6
6
|
//# sourceMappingURL=error-utils.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"error-utils.d.ts","sourceRoot":"","sources":["../../src/utils/error-utils.ts"],"names":[],"mappings":"AAGA;;;GAGG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,
|
|
1
|
+
{"version":3,"file":"error-utils.d.ts","sourceRoot":"","sources":["../../src/utils/error-utils.ts"],"names":[],"mappings":"AAGA;;;GAGG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,CAsEhD"}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { WalletConnectorError } from '@meshconnect/uwc-types';
|
|
2
2
|
/**
|
|
3
3
|
* Parse an error and throw a WalletConnectorError
|
|
4
|
-
* Detects if the error is a user rejection or
|
|
4
|
+
* Detects if the error is a user rejection, expired proposal, or unknown error
|
|
5
5
|
*/
|
|
6
6
|
export function parseError(error) {
|
|
7
7
|
let message;
|
|
8
8
|
let isRejected = false;
|
|
9
|
+
let isExpired = false;
|
|
9
10
|
// Handle different error formats
|
|
10
11
|
if (error && typeof error === 'object') {
|
|
11
12
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
@@ -28,8 +29,21 @@ export function parseError(error) {
|
|
|
28
29
|
else {
|
|
29
30
|
message = String(error);
|
|
30
31
|
}
|
|
31
|
-
|
|
32
|
+
const lowerMessage = message.toLowerCase();
|
|
33
|
+
// Check for expired proposal patterns first
|
|
32
34
|
if (!isRejected) {
|
|
35
|
+
const expiredPatterns = [
|
|
36
|
+
'proposal expired',
|
|
37
|
+
'expired proposal',
|
|
38
|
+
'pairing expired',
|
|
39
|
+
'expired pairing',
|
|
40
|
+
'proposal has expired',
|
|
41
|
+
'pairing has expired'
|
|
42
|
+
];
|
|
43
|
+
isExpired = expiredPatterns.some(pattern => lowerMessage.includes(pattern));
|
|
44
|
+
}
|
|
45
|
+
// If not expired, check for rejection patterns
|
|
46
|
+
if (!isRejected && !isExpired) {
|
|
33
47
|
const rejectionPatterns = [
|
|
34
48
|
'user rejected',
|
|
35
49
|
'user denied',
|
|
@@ -43,11 +57,10 @@ export function parseError(error) {
|
|
|
43
57
|
'user declined',
|
|
44
58
|
'action_rejected'
|
|
45
59
|
];
|
|
46
|
-
const lowerMessage = message.toLowerCase();
|
|
47
60
|
isRejected = rejectionPatterns.some(pattern => lowerMessage.includes(pattern.toLowerCase()));
|
|
48
61
|
}
|
|
49
62
|
const walletError = {
|
|
50
|
-
type: isRejected ? 'rejected' : 'unknown',
|
|
63
|
+
type: isExpired ? 'expired' : isRejected ? 'rejected' : 'unknown',
|
|
51
64
|
message
|
|
52
65
|
};
|
|
53
66
|
throw new WalletConnectorError(walletError);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"error-utils.js","sourceRoot":"","sources":["../../src/utils/error-utils.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAA;AAE7D;;;GAGG;AACH,MAAM,UAAU,UAAU,CAAC,KAAc;IACvC,IAAI,OAAe,CAAA;IACnB,IAAI,UAAU,GAAG,KAAK,CAAA;
|
|
1
|
+
{"version":3,"file":"error-utils.js","sourceRoot":"","sources":["../../src/utils/error-utils.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAA;AAE7D;;;GAGG;AACH,MAAM,UAAU,UAAU,CAAC,KAAc;IACvC,IAAI,OAAe,CAAA;IACnB,IAAI,UAAU,GAAG,KAAK,CAAA;IACtB,IAAI,SAAS,GAAG,KAAK,CAAA;IAErB,iCAAiC;IACjC,IAAI,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QACvC,8DAA8D;QAC9D,MAAM,QAAQ,GAAG,KAAY,CAAA;QAE7B,gCAAgC;QAChC,IAAI,QAAQ,CAAC,IAAI,KAAK,IAAI,IAAI,QAAQ,CAAC,IAAI,KAAK,iBAAiB,EAAE,CAAC;YAClE,UAAU,GAAG,IAAI,CAAA;QACnB,CAAC;QAED,kBAAkB;QAClB,IAAI,QAAQ,CAAC,OAAO,EAAE,CAAC;YACrB,OAAO,GAAG,QAAQ,CAAC,OAAO,CAAA;QAC5B,CAAC;aAAM,IAAI,QAAQ,CAAC,KAAK,EAAE,OAAO,EAAE,CAAC;YACnC,OAAO,GAAG,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAA;QAClC,CAAC;aAAM,CAAC;YACN,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,CAAA;QACzB,CAAC;IACH,CAAC;SAAM,CAAC;QACN,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,CAAA;IACzB,CAAC;IAED,MAAM,YAAY,GAAG,OAAO,CAAC,WAAW,EAAE,CAAA;IAE1C,4CAA4C;IAC5C,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,MAAM,eAAe,GAAG;YACtB,kBAAkB;YAClB,kBAAkB;YAClB,iBAAiB;YACjB,iBAAiB;YACjB,sBAAsB;YACtB,qBAAqB;SACtB,CAAA;QAED,SAAS,GAAG,eAAe,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,YAAY,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAA;IAC7E,CAAC;IAED,+CAA+C;IAC/C,IAAI,CAAC,UAAU,IAAI,CAAC,SAAS,EAAE,CAAC;QAC9B,MAAM,iBAAiB,GAAG;YACxB,eAAe;YACf,aAAa;YACb,gBAAgB;YAChB,eAAe;YACf,kBAAkB;YAClB,gBAAgB;YAChB,mBAAmB;YACnB,kBAAkB;YAClB,kBAAkB;YAClB,eAAe;YACf,iBAAiB;SAClB,CAAA;QAED,UAAU,GAAG,iBAAiB,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAC5C,YAAY,CAAC,QAAQ,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,CAC7C,CAAA;IACH,CAAC;IAED,MAAM,WAAW,GAAgB;QAC/B,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS;QACjE,OAAO;KACR,CAAA;IAED,MAAM,IAAI,oBAAoB,CAAC,WAAW,CAAC,CAAA;AAC7C,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@meshconnect/uwc-wallet-connect-connector",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.13",
|
|
4
4
|
"description": "WalletConnect connector for Universal Wallet Connector",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"buffer": "^6.0.3",
|
|
26
26
|
"ethers": "^6.15.0",
|
|
27
27
|
"tronweb": "^6.0.4",
|
|
28
|
-
"@meshconnect/uwc-types": "0.3.
|
|
28
|
+
"@meshconnect/uwc-types": "0.3.1"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
31
|
"typescript": "^5.9.2"
|
|
@@ -0,0 +1,547 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest'
|
|
2
|
+
import { parseError } from './error-utils'
|
|
3
|
+
import { WalletConnectorError } from '@meshconnect/uwc-types'
|
|
4
|
+
|
|
5
|
+
describe('error-utils', () => {
|
|
6
|
+
describe('parseError', () => {
|
|
7
|
+
describe('expired proposal errors', () => {
|
|
8
|
+
it('should detect "proposal expired" error', () => {
|
|
9
|
+
const error = new Error('proposal expired')
|
|
10
|
+
|
|
11
|
+
expect(() => parseError(error)).toThrow(WalletConnectorError)
|
|
12
|
+
|
|
13
|
+
try {
|
|
14
|
+
parseError(error)
|
|
15
|
+
} catch (err) {
|
|
16
|
+
expect(err).toBeInstanceOf(WalletConnectorError)
|
|
17
|
+
expect((err as WalletConnectorError).type).toBe('expired')
|
|
18
|
+
expect((err as WalletConnectorError).message).toBe('proposal expired')
|
|
19
|
+
}
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
it('should detect "expired proposal" error', () => {
|
|
23
|
+
const error = new Error('expired proposal')
|
|
24
|
+
|
|
25
|
+
try {
|
|
26
|
+
parseError(error)
|
|
27
|
+
} catch (err) {
|
|
28
|
+
expect(err).toBeInstanceOf(WalletConnectorError)
|
|
29
|
+
expect((err as WalletConnectorError).type).toBe('expired')
|
|
30
|
+
expect((err as WalletConnectorError).message).toBe('expired proposal')
|
|
31
|
+
}
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
it('should detect "pairing expired" error', () => {
|
|
35
|
+
const error = new Error('pairing expired')
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
parseError(error)
|
|
39
|
+
} catch (err) {
|
|
40
|
+
expect(err).toBeInstanceOf(WalletConnectorError)
|
|
41
|
+
expect((err as WalletConnectorError).type).toBe('expired')
|
|
42
|
+
}
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
it('should detect "expired pairing" error', () => {
|
|
46
|
+
const error = new Error('expired pairing')
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
parseError(error)
|
|
50
|
+
} catch (err) {
|
|
51
|
+
expect(err).toBeInstanceOf(WalletConnectorError)
|
|
52
|
+
expect((err as WalletConnectorError).type).toBe('expired')
|
|
53
|
+
}
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
it('should detect "proposal has expired" error', () => {
|
|
57
|
+
const error = new Error('The proposal has expired')
|
|
58
|
+
|
|
59
|
+
try {
|
|
60
|
+
parseError(error)
|
|
61
|
+
} catch (err) {
|
|
62
|
+
expect(err).toBeInstanceOf(WalletConnectorError)
|
|
63
|
+
expect((err as WalletConnectorError).type).toBe('expired')
|
|
64
|
+
}
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
it('should detect "pairing has expired" error', () => {
|
|
68
|
+
const error = new Error('The pairing has expired')
|
|
69
|
+
|
|
70
|
+
try {
|
|
71
|
+
parseError(error)
|
|
72
|
+
} catch (err) {
|
|
73
|
+
expect(err).toBeInstanceOf(WalletConnectorError)
|
|
74
|
+
expect((err as WalletConnectorError).type).toBe('expired')
|
|
75
|
+
}
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
it('should be case-insensitive for expired errors', () => {
|
|
79
|
+
const error = new Error('PROPOSAL EXPIRED')
|
|
80
|
+
|
|
81
|
+
try {
|
|
82
|
+
parseError(error)
|
|
83
|
+
} catch (err) {
|
|
84
|
+
expect(err).toBeInstanceOf(WalletConnectorError)
|
|
85
|
+
expect((err as WalletConnectorError).type).toBe('expired')
|
|
86
|
+
}
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
it('should detect expired in longer error messages', () => {
|
|
90
|
+
const error = new Error(
|
|
91
|
+
'Connection failed because the proposal expired after 5 minutes'
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
try {
|
|
95
|
+
parseError(error)
|
|
96
|
+
} catch (err) {
|
|
97
|
+
expect(err).toBeInstanceOf(WalletConnectorError)
|
|
98
|
+
expect((err as WalletConnectorError).type).toBe('expired')
|
|
99
|
+
}
|
|
100
|
+
})
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
describe('rejection errors', () => {
|
|
104
|
+
it('should detect EIP-1193 error code 4001', () => {
|
|
105
|
+
const error = {
|
|
106
|
+
code: 4001,
|
|
107
|
+
message: 'User rejected the request'
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
try {
|
|
111
|
+
parseError(error)
|
|
112
|
+
} catch (err) {
|
|
113
|
+
expect(err).toBeInstanceOf(WalletConnectorError)
|
|
114
|
+
expect((err as WalletConnectorError).type).toBe('rejected')
|
|
115
|
+
expect((err as WalletConnectorError).message).toBe(
|
|
116
|
+
'User rejected the request'
|
|
117
|
+
)
|
|
118
|
+
}
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
it('should detect ACTION_REJECTED error code', () => {
|
|
122
|
+
const error = {
|
|
123
|
+
code: 'ACTION_REJECTED',
|
|
124
|
+
message: 'User rejected the action'
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
try {
|
|
128
|
+
parseError(error)
|
|
129
|
+
} catch (err) {
|
|
130
|
+
expect(err).toBeInstanceOf(WalletConnectorError)
|
|
131
|
+
expect((err as WalletConnectorError).type).toBe('rejected')
|
|
132
|
+
}
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
it('should detect "user rejected" in message', () => {
|
|
136
|
+
const error = new Error('user rejected the transaction')
|
|
137
|
+
|
|
138
|
+
try {
|
|
139
|
+
parseError(error)
|
|
140
|
+
} catch (err) {
|
|
141
|
+
expect(err).toBeInstanceOf(WalletConnectorError)
|
|
142
|
+
expect((err as WalletConnectorError).type).toBe('rejected')
|
|
143
|
+
}
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
it('should detect "user denied" in message', () => {
|
|
147
|
+
const error = new Error('user denied the request')
|
|
148
|
+
|
|
149
|
+
try {
|
|
150
|
+
parseError(error)
|
|
151
|
+
} catch (err) {
|
|
152
|
+
expect(err).toBeInstanceOf(WalletConnectorError)
|
|
153
|
+
expect((err as WalletConnectorError).type).toBe('rejected')
|
|
154
|
+
}
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
it('should detect "user cancelled" in message', () => {
|
|
158
|
+
const error = new Error('user cancelled the operation')
|
|
159
|
+
|
|
160
|
+
try {
|
|
161
|
+
parseError(error)
|
|
162
|
+
} catch (err) {
|
|
163
|
+
expect(err).toBeInstanceOf(WalletConnectorError)
|
|
164
|
+
expect((err as WalletConnectorError).type).toBe('rejected')
|
|
165
|
+
}
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
it('should detect "user canceled" (American spelling) in message', () => {
|
|
169
|
+
const error = new Error('user canceled the operation')
|
|
170
|
+
|
|
171
|
+
try {
|
|
172
|
+
parseError(error)
|
|
173
|
+
} catch (err) {
|
|
174
|
+
expect(err).toBeInstanceOf(WalletConnectorError)
|
|
175
|
+
expect((err as WalletConnectorError).type).toBe('rejected')
|
|
176
|
+
}
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
it('should detect "rejected by user" in message', () => {
|
|
180
|
+
const error = new Error('Transaction was rejected by user')
|
|
181
|
+
|
|
182
|
+
try {
|
|
183
|
+
parseError(error)
|
|
184
|
+
} catch (err) {
|
|
185
|
+
expect(err).toBeInstanceOf(WalletConnectorError)
|
|
186
|
+
expect((err as WalletConnectorError).type).toBe('rejected')
|
|
187
|
+
}
|
|
188
|
+
})
|
|
189
|
+
|
|
190
|
+
it('should detect "denied by user" in message', () => {
|
|
191
|
+
const error = new Error('Access denied by user')
|
|
192
|
+
|
|
193
|
+
try {
|
|
194
|
+
parseError(error)
|
|
195
|
+
} catch (err) {
|
|
196
|
+
expect(err).toBeInstanceOf(WalletConnectorError)
|
|
197
|
+
expect((err as WalletConnectorError).type).toBe('rejected')
|
|
198
|
+
}
|
|
199
|
+
})
|
|
200
|
+
|
|
201
|
+
it('should detect "cancelled by user" in message', () => {
|
|
202
|
+
const error = new Error('Operation cancelled by user')
|
|
203
|
+
|
|
204
|
+
try {
|
|
205
|
+
parseError(error)
|
|
206
|
+
} catch (err) {
|
|
207
|
+
expect(err).toBeInstanceOf(WalletConnectorError)
|
|
208
|
+
expect((err as WalletConnectorError).type).toBe('rejected')
|
|
209
|
+
}
|
|
210
|
+
})
|
|
211
|
+
|
|
212
|
+
it('should detect "canceled by user" in message', () => {
|
|
213
|
+
const error = new Error('Operation canceled by user')
|
|
214
|
+
|
|
215
|
+
try {
|
|
216
|
+
parseError(error)
|
|
217
|
+
} catch (err) {
|
|
218
|
+
expect(err).toBeInstanceOf(WalletConnectorError)
|
|
219
|
+
expect((err as WalletConnectorError).type).toBe('rejected')
|
|
220
|
+
}
|
|
221
|
+
})
|
|
222
|
+
|
|
223
|
+
it('should detect "user disapproved" in message', () => {
|
|
224
|
+
const error = new Error('user disapproved the transaction')
|
|
225
|
+
|
|
226
|
+
try {
|
|
227
|
+
parseError(error)
|
|
228
|
+
} catch (err) {
|
|
229
|
+
expect(err).toBeInstanceOf(WalletConnectorError)
|
|
230
|
+
expect((err as WalletConnectorError).type).toBe('rejected')
|
|
231
|
+
}
|
|
232
|
+
})
|
|
233
|
+
|
|
234
|
+
it('should detect "user declined" in message', () => {
|
|
235
|
+
const error = new Error('user declined the request')
|
|
236
|
+
|
|
237
|
+
try {
|
|
238
|
+
parseError(error)
|
|
239
|
+
} catch (err) {
|
|
240
|
+
expect(err).toBeInstanceOf(WalletConnectorError)
|
|
241
|
+
expect((err as WalletConnectorError).type).toBe('rejected')
|
|
242
|
+
}
|
|
243
|
+
})
|
|
244
|
+
|
|
245
|
+
it('should detect "action_rejected" in message', () => {
|
|
246
|
+
const error = new Error('action_rejected by wallet')
|
|
247
|
+
|
|
248
|
+
try {
|
|
249
|
+
parseError(error)
|
|
250
|
+
} catch (err) {
|
|
251
|
+
expect(err).toBeInstanceOf(WalletConnectorError)
|
|
252
|
+
expect((err as WalletConnectorError).type).toBe('rejected')
|
|
253
|
+
}
|
|
254
|
+
})
|
|
255
|
+
|
|
256
|
+
it('should be case-insensitive for rejection errors', () => {
|
|
257
|
+
const error = new Error('USER REJECTED THE REQUEST')
|
|
258
|
+
|
|
259
|
+
try {
|
|
260
|
+
parseError(error)
|
|
261
|
+
} catch (err) {
|
|
262
|
+
expect(err).toBeInstanceOf(WalletConnectorError)
|
|
263
|
+
expect((err as WalletConnectorError).type).toBe('rejected')
|
|
264
|
+
}
|
|
265
|
+
})
|
|
266
|
+
})
|
|
267
|
+
|
|
268
|
+
describe('unknown errors', () => {
|
|
269
|
+
it('should classify unrecognized errors as unknown', () => {
|
|
270
|
+
const error = new Error('Network connection failed')
|
|
271
|
+
|
|
272
|
+
try {
|
|
273
|
+
parseError(error)
|
|
274
|
+
} catch (err) {
|
|
275
|
+
expect(err).toBeInstanceOf(WalletConnectorError)
|
|
276
|
+
expect((err as WalletConnectorError).type).toBe('unknown')
|
|
277
|
+
expect((err as WalletConnectorError).message).toBe(
|
|
278
|
+
'Network connection failed'
|
|
279
|
+
)
|
|
280
|
+
}
|
|
281
|
+
})
|
|
282
|
+
|
|
283
|
+
it('should handle generic Error objects', () => {
|
|
284
|
+
const error = new Error('Something went wrong')
|
|
285
|
+
|
|
286
|
+
try {
|
|
287
|
+
parseError(error)
|
|
288
|
+
} catch (err) {
|
|
289
|
+
expect(err).toBeInstanceOf(WalletConnectorError)
|
|
290
|
+
expect((err as WalletConnectorError).type).toBe('unknown')
|
|
291
|
+
}
|
|
292
|
+
})
|
|
293
|
+
|
|
294
|
+
it('should handle timeout errors as unknown', () => {
|
|
295
|
+
const error = new Error('Request timed out')
|
|
296
|
+
|
|
297
|
+
try {
|
|
298
|
+
parseError(error)
|
|
299
|
+
} catch (err) {
|
|
300
|
+
expect(err).toBeInstanceOf(WalletConnectorError)
|
|
301
|
+
expect((err as WalletConnectorError).type).toBe('unknown')
|
|
302
|
+
}
|
|
303
|
+
})
|
|
304
|
+
})
|
|
305
|
+
|
|
306
|
+
describe('error message extraction', () => {
|
|
307
|
+
it('should extract message from Error object', () => {
|
|
308
|
+
const error = new Error('Custom error message')
|
|
309
|
+
|
|
310
|
+
try {
|
|
311
|
+
parseError(error)
|
|
312
|
+
} catch (err) {
|
|
313
|
+
expect((err as WalletConnectorError).message).toBe(
|
|
314
|
+
'Custom error message'
|
|
315
|
+
)
|
|
316
|
+
}
|
|
317
|
+
})
|
|
318
|
+
|
|
319
|
+
it('should extract message from error.message property', () => {
|
|
320
|
+
const error = {
|
|
321
|
+
message: 'Error from object with message'
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
try {
|
|
325
|
+
parseError(error)
|
|
326
|
+
} catch (err) {
|
|
327
|
+
expect((err as WalletConnectorError).message).toBe(
|
|
328
|
+
'Error from object with message'
|
|
329
|
+
)
|
|
330
|
+
}
|
|
331
|
+
})
|
|
332
|
+
|
|
333
|
+
it('should extract message from error.error.message nested property', () => {
|
|
334
|
+
const error = {
|
|
335
|
+
error: {
|
|
336
|
+
message: 'Nested error message'
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
try {
|
|
341
|
+
parseError(error)
|
|
342
|
+
} catch (err) {
|
|
343
|
+
expect((err as WalletConnectorError).message).toBe(
|
|
344
|
+
'Nested error message'
|
|
345
|
+
)
|
|
346
|
+
}
|
|
347
|
+
})
|
|
348
|
+
|
|
349
|
+
it('should convert non-object errors to string', () => {
|
|
350
|
+
const error = 'String error message'
|
|
351
|
+
|
|
352
|
+
try {
|
|
353
|
+
parseError(error)
|
|
354
|
+
} catch (err) {
|
|
355
|
+
expect((err as WalletConnectorError).message).toBe(
|
|
356
|
+
'String error message'
|
|
357
|
+
)
|
|
358
|
+
}
|
|
359
|
+
})
|
|
360
|
+
|
|
361
|
+
it('should handle null error', () => {
|
|
362
|
+
const error = null
|
|
363
|
+
|
|
364
|
+
try {
|
|
365
|
+
parseError(error)
|
|
366
|
+
} catch (err) {
|
|
367
|
+
expect((err as WalletConnectorError).message).toBe('null')
|
|
368
|
+
}
|
|
369
|
+
})
|
|
370
|
+
|
|
371
|
+
it('should handle undefined error', () => {
|
|
372
|
+
const error = undefined
|
|
373
|
+
|
|
374
|
+
try {
|
|
375
|
+
parseError(error)
|
|
376
|
+
} catch (err) {
|
|
377
|
+
expect((err as WalletConnectorError).message).toBe('undefined')
|
|
378
|
+
}
|
|
379
|
+
})
|
|
380
|
+
|
|
381
|
+
it('should handle number as error', () => {
|
|
382
|
+
const error = 404
|
|
383
|
+
|
|
384
|
+
try {
|
|
385
|
+
parseError(error)
|
|
386
|
+
} catch (err) {
|
|
387
|
+
expect((err as WalletConnectorError).message).toBe('404')
|
|
388
|
+
}
|
|
389
|
+
})
|
|
390
|
+
})
|
|
391
|
+
|
|
392
|
+
describe('priority order', () => {
|
|
393
|
+
it('should prioritize expired over rejection patterns', () => {
|
|
394
|
+
const error = new Error('user rejected because the proposal expired')
|
|
395
|
+
|
|
396
|
+
try {
|
|
397
|
+
parseError(error)
|
|
398
|
+
} catch (err) {
|
|
399
|
+
expect(err).toBeInstanceOf(WalletConnectorError)
|
|
400
|
+
expect((err as WalletConnectorError).type).toBe('expired')
|
|
401
|
+
}
|
|
402
|
+
})
|
|
403
|
+
|
|
404
|
+
it('should prioritize EIP-1193 code 4001 for rejection', () => {
|
|
405
|
+
const error = {
|
|
406
|
+
code: 4001,
|
|
407
|
+
message: 'Some other message without rejection keywords'
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
try {
|
|
411
|
+
parseError(error)
|
|
412
|
+
} catch (err) {
|
|
413
|
+
expect(err).toBeInstanceOf(WalletConnectorError)
|
|
414
|
+
expect((err as WalletConnectorError).type).toBe('rejected')
|
|
415
|
+
}
|
|
416
|
+
})
|
|
417
|
+
|
|
418
|
+
it('should check rejection patterns only if code check fails', () => {
|
|
419
|
+
const error = {
|
|
420
|
+
code: 5000,
|
|
421
|
+
message: 'user rejected the transaction'
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
try {
|
|
425
|
+
parseError(error)
|
|
426
|
+
} catch (err) {
|
|
427
|
+
expect(err).toBeInstanceOf(WalletConnectorError)
|
|
428
|
+
expect((err as WalletConnectorError).type).toBe('rejected')
|
|
429
|
+
}
|
|
430
|
+
})
|
|
431
|
+
})
|
|
432
|
+
|
|
433
|
+
describe('real-world error scenarios', () => {
|
|
434
|
+
it('should handle MetaMask user rejection', () => {
|
|
435
|
+
const error = {
|
|
436
|
+
code: 4001,
|
|
437
|
+
message: 'MetaMask Tx Signature: User denied transaction signature.'
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
try {
|
|
441
|
+
parseError(error)
|
|
442
|
+
} catch (err) {
|
|
443
|
+
expect(err).toBeInstanceOf(WalletConnectorError)
|
|
444
|
+
expect((err as WalletConnectorError).type).toBe('rejected')
|
|
445
|
+
}
|
|
446
|
+
})
|
|
447
|
+
|
|
448
|
+
it('should handle WalletConnect expired proposal', () => {
|
|
449
|
+
const error = new Error(
|
|
450
|
+
'Proposal expired. Please try connecting again.'
|
|
451
|
+
)
|
|
452
|
+
|
|
453
|
+
try {
|
|
454
|
+
parseError(error)
|
|
455
|
+
} catch (err) {
|
|
456
|
+
expect(err).toBeInstanceOf(WalletConnectorError)
|
|
457
|
+
expect((err as WalletConnectorError).type).toBe('expired')
|
|
458
|
+
}
|
|
459
|
+
})
|
|
460
|
+
|
|
461
|
+
it('should handle network errors as unknown', () => {
|
|
462
|
+
const error = {
|
|
463
|
+
message: 'Failed to fetch',
|
|
464
|
+
name: 'NetworkError'
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
try {
|
|
468
|
+
parseError(error)
|
|
469
|
+
} catch (err) {
|
|
470
|
+
expect(err).toBeInstanceOf(WalletConnectorError)
|
|
471
|
+
expect((err as WalletConnectorError).type).toBe('unknown')
|
|
472
|
+
expect((err as WalletConnectorError).message).toBe('Failed to fetch')
|
|
473
|
+
}
|
|
474
|
+
})
|
|
475
|
+
|
|
476
|
+
it('should handle JSON-RPC errors as unknown', () => {
|
|
477
|
+
const error = {
|
|
478
|
+
code: -32603,
|
|
479
|
+
message: 'Internal JSON-RPC error'
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
try {
|
|
483
|
+
parseError(error)
|
|
484
|
+
} catch (err) {
|
|
485
|
+
expect(err).toBeInstanceOf(WalletConnectorError)
|
|
486
|
+
expect((err as WalletConnectorError).type).toBe('unknown')
|
|
487
|
+
}
|
|
488
|
+
})
|
|
489
|
+
})
|
|
490
|
+
|
|
491
|
+
describe('edge cases', () => {
|
|
492
|
+
it('should handle empty string error', () => {
|
|
493
|
+
const error = ''
|
|
494
|
+
|
|
495
|
+
try {
|
|
496
|
+
parseError(error)
|
|
497
|
+
} catch (err) {
|
|
498
|
+
expect(err).toBeInstanceOf(WalletConnectorError)
|
|
499
|
+
expect((err as WalletConnectorError).type).toBe('unknown')
|
|
500
|
+
expect((err as WalletConnectorError).message).toBe('')
|
|
501
|
+
}
|
|
502
|
+
})
|
|
503
|
+
|
|
504
|
+
it('should handle error with only whitespace', () => {
|
|
505
|
+
const error = new Error(' ')
|
|
506
|
+
|
|
507
|
+
try {
|
|
508
|
+
parseError(error)
|
|
509
|
+
} catch (err) {
|
|
510
|
+
expect(err).toBeInstanceOf(WalletConnectorError)
|
|
511
|
+
expect((err as WalletConnectorError).type).toBe('unknown')
|
|
512
|
+
}
|
|
513
|
+
})
|
|
514
|
+
|
|
515
|
+
it('should handle object without message property', () => {
|
|
516
|
+
const error = {
|
|
517
|
+
code: 123,
|
|
518
|
+
data: 'some data'
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
try {
|
|
522
|
+
parseError(error)
|
|
523
|
+
} catch (err) {
|
|
524
|
+
expect(err).toBeInstanceOf(WalletConnectorError)
|
|
525
|
+
expect((err as WalletConnectorError).type).toBe('unknown')
|
|
526
|
+
}
|
|
527
|
+
})
|
|
528
|
+
|
|
529
|
+
it('should handle array as error', () => {
|
|
530
|
+
const error = ['error1', 'error2']
|
|
531
|
+
|
|
532
|
+
try {
|
|
533
|
+
parseError(error)
|
|
534
|
+
} catch (err) {
|
|
535
|
+
expect(err).toBeInstanceOf(WalletConnectorError)
|
|
536
|
+
expect((err as WalletConnectorError).message).toBe('error1,error2')
|
|
537
|
+
}
|
|
538
|
+
})
|
|
539
|
+
|
|
540
|
+
it('should never return - always throws', () => {
|
|
541
|
+
const error = new Error('test')
|
|
542
|
+
|
|
543
|
+
expect(() => parseError(error)).toThrow()
|
|
544
|
+
})
|
|
545
|
+
})
|
|
546
|
+
})
|
|
547
|
+
})
|
package/src/utils/error-utils.ts
CHANGED
|
@@ -3,11 +3,12 @@ import { WalletConnectorError } from '@meshconnect/uwc-types'
|
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Parse an error and throw a WalletConnectorError
|
|
6
|
-
* Detects if the error is a user rejection or
|
|
6
|
+
* Detects if the error is a user rejection, expired proposal, or unknown error
|
|
7
7
|
*/
|
|
8
8
|
export function parseError(error: unknown): never {
|
|
9
9
|
let message: string
|
|
10
10
|
let isRejected = false
|
|
11
|
+
let isExpired = false
|
|
11
12
|
|
|
12
13
|
// Handle different error formats
|
|
13
14
|
if (error && typeof error === 'object') {
|
|
@@ -31,8 +32,24 @@ export function parseError(error: unknown): never {
|
|
|
31
32
|
message = String(error)
|
|
32
33
|
}
|
|
33
34
|
|
|
34
|
-
|
|
35
|
+
const lowerMessage = message.toLowerCase()
|
|
36
|
+
|
|
37
|
+
// Check for expired proposal patterns first
|
|
35
38
|
if (!isRejected) {
|
|
39
|
+
const expiredPatterns = [
|
|
40
|
+
'proposal expired',
|
|
41
|
+
'expired proposal',
|
|
42
|
+
'pairing expired',
|
|
43
|
+
'expired pairing',
|
|
44
|
+
'proposal has expired',
|
|
45
|
+
'pairing has expired'
|
|
46
|
+
]
|
|
47
|
+
|
|
48
|
+
isExpired = expiredPatterns.some(pattern => lowerMessage.includes(pattern))
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// If not expired, check for rejection patterns
|
|
52
|
+
if (!isRejected && !isExpired) {
|
|
36
53
|
const rejectionPatterns = [
|
|
37
54
|
'user rejected',
|
|
38
55
|
'user denied',
|
|
@@ -47,14 +64,13 @@ export function parseError(error: unknown): never {
|
|
|
47
64
|
'action_rejected'
|
|
48
65
|
]
|
|
49
66
|
|
|
50
|
-
const lowerMessage = message.toLowerCase()
|
|
51
67
|
isRejected = rejectionPatterns.some(pattern =>
|
|
52
68
|
lowerMessage.includes(pattern.toLowerCase())
|
|
53
69
|
)
|
|
54
70
|
}
|
|
55
71
|
|
|
56
72
|
const walletError: WalletError = {
|
|
57
|
-
type: isRejected ? 'rejected' : 'unknown',
|
|
73
|
+
type: isExpired ? 'expired' : isRejected ? 'rejected' : 'unknown',
|
|
58
74
|
message
|
|
59
75
|
}
|
|
60
76
|
|