@rango-dev/wallets-react 0.0.0-experimental-c6bea4d8-20260930 → 0.0.0-experimental-2938686c-20261012
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.
- package/dist/events.d.ts +49 -0
- package/dist/events.d.ts.map +1 -0
- package/dist/hub/autoConnect.d.ts +2 -0
- package/dist/hub/autoConnect.d.ts.map +1 -1
- package/dist/hub/constants.d.ts +1 -0
- package/dist/hub/constants.d.ts.map +1 -1
- package/dist/hub/errors.d.ts +2 -3
- package/dist/hub/errors.d.ts.map +1 -1
- package/dist/hub/helpers.d.ts +0 -3
- package/dist/hub/helpers.d.ts.map +1 -1
- package/dist/hub/useHubAdapter.d.ts.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +4 -4
- package/dist/types.d.ts +3 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/walletEvents.test.d.ts +2 -0
- package/dist/walletEvents.test.d.ts.map +1 -0
- package/package.json +13 -12
- package/src/events.ts +99 -0
- package/src/hub/autoConnect.test.ts +51 -18
- package/src/hub/autoConnect.ts +55 -3
- package/src/hub/constants.ts +12 -0
- package/src/hub/errors.test.ts +50 -29
- package/src/hub/errors.ts +27 -21
- package/src/hub/helpers.ts +0 -14
- package/src/hub/useHubAdapter.ts +85 -29
- package/src/index.ts +12 -0
- package/src/types.ts +3 -0
- package/src/walletEvents.test.tsx +533 -0
- package/dist/helpers.d.ts +0 -3
- package/dist/helpers.d.ts.map +0 -1
- package/dist/hub/helpers.test.d.ts +0 -2
- package/dist/hub/helpers.test.d.ts.map +0 -1
- package/src/helpers.ts +0 -43
- package/src/hub/helpers.test.ts +0 -86
package/src/events.ts
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import type { WalletType } from '@hub3js/core';
|
|
2
|
+
import type { Namespace } from '@hub3js/namespaces';
|
|
3
|
+
import type { BlockchainMeta } from 'rango-types';
|
|
4
|
+
|
|
5
|
+
import { getBlockChainNameFromId } from '@rango-dev/internal-blockchains';
|
|
6
|
+
|
|
7
|
+
import { WALLET_DETECTED_SKIPPED_IDS } from './hub/constants.js';
|
|
8
|
+
|
|
9
|
+
export const WalletEventChannel = 'walletEvent';
|
|
10
|
+
|
|
11
|
+
export enum WalletEventTypes {
|
|
12
|
+
WALLET_DETECTED = 'walletDetected',
|
|
13
|
+
WALLET_CONNECT_INITIATED = 'walletConnectInitiated',
|
|
14
|
+
WALLET_CONNECTED = 'walletConnected',
|
|
15
|
+
WALLET_CONNECT_FAILED = 'walletConnectFailed',
|
|
16
|
+
WALLET_DISCONNECTED = 'walletDisconnected',
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export type WalletConnectOrigin = 'manual' | 'auto';
|
|
20
|
+
|
|
21
|
+
type WalletConnectEventPayload = {
|
|
22
|
+
/** Wallet type id, e.g. `metamask`. */
|
|
23
|
+
walletName: WalletType;
|
|
24
|
+
namespace: Namespace;
|
|
25
|
+
/** Rango blockchain name (e.g. `ETH`), or `null` when it isn't known. */
|
|
26
|
+
chain: string | null;
|
|
27
|
+
origin: WalletConnectOrigin;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export type WalletDetectedEventPayload = {
|
|
31
|
+
walletName: WalletType;
|
|
32
|
+
};
|
|
33
|
+
export type WalletConnectInitiatedEventPayload = WalletConnectEventPayload;
|
|
34
|
+
export type WalletConnectedEventPayload = WalletConnectEventPayload;
|
|
35
|
+
export type WalletConnectFailedEventPayload = WalletConnectEventPayload;
|
|
36
|
+
export type WalletDisconnectedEventPayload = {
|
|
37
|
+
walletName: WalletType;
|
|
38
|
+
namespace: Namespace;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
type EventData<T extends WalletEventTypes, U> = { type: T; payload: U };
|
|
42
|
+
|
|
43
|
+
export type WalletEventData =
|
|
44
|
+
| EventData<WalletEventTypes.WALLET_DETECTED, WalletDetectedEventPayload>
|
|
45
|
+
| EventData<
|
|
46
|
+
WalletEventTypes.WALLET_CONNECT_INITIATED,
|
|
47
|
+
WalletConnectInitiatedEventPayload
|
|
48
|
+
>
|
|
49
|
+
| EventData<WalletEventTypes.WALLET_CONNECTED, WalletConnectedEventPayload>
|
|
50
|
+
| EventData<
|
|
51
|
+
WalletEventTypes.WALLET_CONNECT_FAILED,
|
|
52
|
+
WalletConnectFailedEventPayload
|
|
53
|
+
>
|
|
54
|
+
| EventData<
|
|
55
|
+
WalletEventTypes.WALLET_DISCONNECTED,
|
|
56
|
+
WalletDisconnectedEventPayload
|
|
57
|
+
>;
|
|
58
|
+
|
|
59
|
+
export type WalletEvents = {
|
|
60
|
+
[WalletEventChannel]: WalletEventData;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export interface Emitter<Events extends Record<string, unknown>> {
|
|
64
|
+
emit<K extends keyof Events>(type: K, event: Events[K]): void;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export type WalletEventEmitter = Emitter<WalletEvents>;
|
|
68
|
+
|
|
69
|
+
const detectedWallets = new Set<WalletType>();
|
|
70
|
+
|
|
71
|
+
export function emitWalletDetectedOnce(
|
|
72
|
+
walletName: WalletType,
|
|
73
|
+
emitter?: WalletEventEmitter
|
|
74
|
+
): void {
|
|
75
|
+
if (
|
|
76
|
+
detectedWallets.has(walletName) ||
|
|
77
|
+
WALLET_DETECTED_SKIPPED_IDS.includes(walletName)
|
|
78
|
+
) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
detectedWallets.add(walletName);
|
|
82
|
+
emitter?.emit(WalletEventChannel, {
|
|
83
|
+
type: WalletEventTypes.WALLET_DETECTED,
|
|
84
|
+
payload: { walletName },
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Connect results may carry an EVM chain id (e.g. `0x1`); events always carry a Rango blockchain name.
|
|
90
|
+
*/
|
|
91
|
+
export function toBlockchainName(
|
|
92
|
+
network: string | null | undefined,
|
|
93
|
+
allBlockChains: BlockchainMeta[] | undefined
|
|
94
|
+
): string | null {
|
|
95
|
+
if (!network) {
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
return getBlockChainNameFromId(network, allBlockChains || []);
|
|
99
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Hub } from '@hub3js/core';
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { Hub3Error } from '@hub3js/core';
|
|
4
4
|
import { error as logError } from '@rango-dev/logging-core';
|
|
5
5
|
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
6
6
|
|
|
@@ -19,9 +19,8 @@ type FakeNamespace = {
|
|
|
19
19
|
connect: () => Promise<string[]>;
|
|
20
20
|
};
|
|
21
21
|
|
|
22
|
-
function
|
|
23
|
-
return new
|
|
24
|
-
type: ConnectionErrorType.Unknown,
|
|
22
|
+
function unexpectedError() {
|
|
23
|
+
return new Hub3Error('PROVIDER_UNEXPECTED', 'Something went wrong', {
|
|
25
24
|
cause: new Error('Something went wrong'),
|
|
26
25
|
});
|
|
27
26
|
}
|
|
@@ -79,8 +78,8 @@ describe('autoConnect', () => {
|
|
|
79
78
|
vi.restoreAllMocks();
|
|
80
79
|
});
|
|
81
80
|
|
|
82
|
-
it('report a saved namespace that fails to reconnect with an
|
|
83
|
-
const error =
|
|
81
|
+
it('report a saved namespace that fails to reconnect with an unexpected error', async () => {
|
|
82
|
+
const error = unexpectedError();
|
|
84
83
|
const solana = fakeNamespace('Solana', {
|
|
85
84
|
connect: vi.fn(async () => Promise.reject(error)),
|
|
86
85
|
});
|
|
@@ -92,8 +91,8 @@ describe('autoConnect', () => {
|
|
|
92
91
|
expect(logError).toHaveBeenCalledOnce();
|
|
93
92
|
expect(logError).toHaveBeenCalledWith(error, {
|
|
94
93
|
tags: {
|
|
95
|
-
name: '
|
|
96
|
-
type:
|
|
94
|
+
name: 'Hub3Error',
|
|
95
|
+
type: 'PROVIDER_UNEXPECTED',
|
|
97
96
|
walletType: WALLET,
|
|
98
97
|
namespace: 'Solana',
|
|
99
98
|
origin: 'auto',
|
|
@@ -101,11 +100,45 @@ describe('autoConnect', () => {
|
|
|
101
100
|
});
|
|
102
101
|
});
|
|
103
102
|
|
|
103
|
+
it('report a plain error with the auto origin and no type', async () => {
|
|
104
|
+
const error = new Error('Before hook failed');
|
|
105
|
+
const solana = fakeNamespace('Solana', {
|
|
106
|
+
connect: vi.fn(async () => Promise.reject(error)),
|
|
107
|
+
});
|
|
108
|
+
const sui = fakeNamespace('Sui');
|
|
109
|
+
|
|
110
|
+
runAutoConnect([solana, sui]);
|
|
111
|
+
await settle(() => expect(sui.connect).toHaveBeenCalled());
|
|
112
|
+
|
|
113
|
+
expect(logError).toHaveBeenCalledOnce();
|
|
114
|
+
expect(logError).toHaveBeenCalledWith(error, {
|
|
115
|
+
tags: {
|
|
116
|
+
name: 'Error',
|
|
117
|
+
walletType: WALLET,
|
|
118
|
+
namespace: 'Solana',
|
|
119
|
+
origin: 'auto',
|
|
120
|
+
},
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it.each(['PROVIDER_USER_REJECTED_REQUEST', 'PROVIDER_LOCKED'] as const)(
|
|
125
|
+
'skip a namespace that fails with %s',
|
|
126
|
+
async (type) => {
|
|
127
|
+
const solana = fakeNamespace('Solana', {
|
|
128
|
+
connect: vi.fn(async () => Promise.reject(new Hub3Error(type))),
|
|
129
|
+
});
|
|
130
|
+
const sui = fakeNamespace('Sui');
|
|
131
|
+
|
|
132
|
+
runAutoConnect([solana, sui]);
|
|
133
|
+
await settle(() => expect(sui.connect).toHaveBeenCalled());
|
|
134
|
+
|
|
135
|
+
expect(logError).not.toHaveBeenCalled();
|
|
136
|
+
}
|
|
137
|
+
);
|
|
138
|
+
|
|
104
139
|
it('skip a namespace that fails its eager connect check', async () => {
|
|
105
140
|
const solana = fakeNamespace('Solana', {
|
|
106
|
-
canEagerConnect: vi.fn(async () =>
|
|
107
|
-
Promise.reject(unknownConnectionError())
|
|
108
|
-
),
|
|
141
|
+
canEagerConnect: vi.fn(async () => Promise.reject(unexpectedError())),
|
|
109
142
|
});
|
|
110
143
|
const sui = fakeNamespace('Sui');
|
|
111
144
|
|
|
@@ -117,11 +150,11 @@ describe('autoConnect', () => {
|
|
|
117
150
|
});
|
|
118
151
|
|
|
119
152
|
it('report only the namespace failure when no namespace connects', async () => {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
153
|
+
runAutoConnect([
|
|
154
|
+
fakeNamespace('Solana', {
|
|
155
|
+
connect: vi.fn(async () => Promise.reject(unexpectedError())),
|
|
156
|
+
}),
|
|
157
|
+
]);
|
|
125
158
|
await settle(() =>
|
|
126
159
|
expect(console.warn).toHaveBeenCalledWith(
|
|
127
160
|
new Error(`No namespace connected for ${WALLET}`)
|
|
@@ -130,9 +163,9 @@ describe('autoConnect', () => {
|
|
|
130
163
|
|
|
131
164
|
expect(logError).toHaveBeenCalledOnce();
|
|
132
165
|
expect(logError).toHaveBeenCalledWith(
|
|
133
|
-
expect.any(
|
|
166
|
+
expect.any(Hub3Error),
|
|
134
167
|
expect.objectContaining({
|
|
135
|
-
tags: expect.objectContaining({ namespace: 'Solana' }),
|
|
168
|
+
tags: expect.objectContaining({ namespace: 'Solana', origin: 'auto' }),
|
|
136
169
|
})
|
|
137
170
|
);
|
|
138
171
|
});
|
package/src/hub/autoConnect.ts
CHANGED
|
@@ -7,6 +7,13 @@ import type { Accounts, AccountsWithActiveChain } from '@hub3js/std/types';
|
|
|
7
7
|
|
|
8
8
|
import { Result } from 'ts-results';
|
|
9
9
|
|
|
10
|
+
import {
|
|
11
|
+
toBlockchainName,
|
|
12
|
+
WalletEventChannel,
|
|
13
|
+
type WalletEventEmitter,
|
|
14
|
+
WalletEventTypes,
|
|
15
|
+
} from '../events.js';
|
|
16
|
+
|
|
10
17
|
import { HUB_LAST_CONNECTED_WALLETS } from './constants.js';
|
|
11
18
|
import { reportConnectionFailure } from './errors.js';
|
|
12
19
|
import { runSequentiallyWithoutFailure } from './helpers.js';
|
|
@@ -14,8 +21,21 @@ import { LastConnectedWalletsFromStorage } from './lastConnectedWallets.js';
|
|
|
14
21
|
import {
|
|
15
22
|
convertNamespaceNetworkToEvmChainId,
|
|
16
23
|
isEvmNamespace,
|
|
24
|
+
transformHubResultToLegacyResult,
|
|
17
25
|
} from './utils.js';
|
|
18
26
|
|
|
27
|
+
function createAutoConnectEventPayload(
|
|
28
|
+
walletName: WalletType,
|
|
29
|
+
namespaceInput: NamespaceInputForConnect
|
|
30
|
+
) {
|
|
31
|
+
return {
|
|
32
|
+
walletName,
|
|
33
|
+
namespace: namespaceInput.namespace,
|
|
34
|
+
chain: namespaceInput.network || null,
|
|
35
|
+
origin: 'auto' as const,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
19
39
|
function isEvmNamespaceInput(
|
|
20
40
|
namespace: NamespaceInputForConnect
|
|
21
41
|
): namespace is NamespaceInputForConnect<'EVM'> {
|
|
@@ -36,9 +56,10 @@ async function eagerConnect(
|
|
|
36
56
|
params: {
|
|
37
57
|
getHub: () => Hub;
|
|
38
58
|
allBlockChains: UseAdapterParams['allBlockChains'];
|
|
59
|
+
emitter?: WalletEventEmitter;
|
|
39
60
|
}
|
|
40
61
|
) {
|
|
41
|
-
const { getHub, allBlockChains } = params;
|
|
62
|
+
const { getHub, allBlockChains, emitter } = params;
|
|
42
63
|
const wallet = getHub().get(type);
|
|
43
64
|
if (!wallet) {
|
|
44
65
|
throw new Error(
|
|
@@ -82,8 +103,19 @@ async function eagerConnect(
|
|
|
82
103
|
} else {
|
|
83
104
|
connectNamespacePromise = async () => namespace.connect();
|
|
84
105
|
}
|
|
106
|
+
const eventPayload = createAutoConnectEventPayload(type, info);
|
|
85
107
|
try {
|
|
86
|
-
await connectNamespacePromise();
|
|
108
|
+
const result = await connectNamespacePromise();
|
|
109
|
+
emitter?.emit(WalletEventChannel, {
|
|
110
|
+
type: WalletEventTypes.WALLET_CONNECTED,
|
|
111
|
+
payload: {
|
|
112
|
+
...eventPayload,
|
|
113
|
+
chain: toBlockchainName(
|
|
114
|
+
transformHubResultToLegacyResult(result).network,
|
|
115
|
+
allBlockChains
|
|
116
|
+
),
|
|
117
|
+
},
|
|
118
|
+
});
|
|
87
119
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
88
120
|
} catch (e: any) {
|
|
89
121
|
reportConnectionFailure(e, {
|
|
@@ -91,6 +123,10 @@ async function eagerConnect(
|
|
|
91
123
|
namespace: info.namespace,
|
|
92
124
|
origin: 'auto',
|
|
93
125
|
});
|
|
126
|
+
emitter?.emit(WalletEventChannel, {
|
|
127
|
+
type: WalletEventTypes.WALLET_CONNECT_FAILED,
|
|
128
|
+
payload: eventPayload,
|
|
129
|
+
});
|
|
94
130
|
/*
|
|
95
131
|
* Since we check for connect failures using `instanceof Error`
|
|
96
132
|
* this check is added here to make sure the thrown error always is an instance of `Error`
|
|
@@ -188,8 +224,9 @@ export async function autoConnect(deps: {
|
|
|
188
224
|
getHub: () => Hub;
|
|
189
225
|
allBlockChains: UseAdapterParams['allBlockChains'];
|
|
190
226
|
wallets?: (WalletType | Provider)[];
|
|
227
|
+
emitter?: WalletEventEmitter;
|
|
191
228
|
}): Promise<void> {
|
|
192
|
-
const { getHub, allBlockChains, wallets } = deps;
|
|
229
|
+
const { getHub, allBlockChains, wallets, emitter } = deps;
|
|
193
230
|
const lastConnectedWallets = lastConnectedWalletsFromStorage.list();
|
|
194
231
|
const walletIds = Object.keys(lastConnectedWallets);
|
|
195
232
|
|
|
@@ -225,9 +262,23 @@ export async function autoConnect(deps: {
|
|
|
225
262
|
return;
|
|
226
263
|
}
|
|
227
264
|
|
|
265
|
+
lastConnectedNamespaces.forEach((namespace) => {
|
|
266
|
+
emitter?.emit(WalletEventChannel, {
|
|
267
|
+
type: WalletEventTypes.WALLET_CONNECT_INITIATED,
|
|
268
|
+
payload: createAutoConnectEventPayload(providerName, namespace),
|
|
269
|
+
});
|
|
270
|
+
});
|
|
271
|
+
|
|
228
272
|
const { successNamespaces, failedNamespaces } =
|
|
229
273
|
await tryRunCanEagerConnect(lastConnectedNamespaces, wallet);
|
|
230
274
|
|
|
275
|
+
failedNamespaces.forEach((namespace) => {
|
|
276
|
+
emitter?.emit(WalletEventChannel, {
|
|
277
|
+
type: WalletEventTypes.WALLET_CONNECT_FAILED,
|
|
278
|
+
payload: createAutoConnectEventPayload(providerName, namespace),
|
|
279
|
+
});
|
|
280
|
+
});
|
|
281
|
+
|
|
231
282
|
if (!successNamespaces.length) {
|
|
232
283
|
walletsToRemoveFromPersistence.push(providerName);
|
|
233
284
|
return;
|
|
@@ -241,6 +292,7 @@ export async function autoConnect(deps: {
|
|
|
241
292
|
eagerConnect(providerName, successNamespaces, {
|
|
242
293
|
allBlockChains,
|
|
243
294
|
getHub,
|
|
295
|
+
emitter,
|
|
244
296
|
}).catch((error) => console.warn(error))
|
|
245
297
|
);
|
|
246
298
|
});
|
package/src/hub/constants.ts
CHANGED
|
@@ -1 +1,13 @@
|
|
|
1
|
+
import { WalletTypes } from '@rango-dev/provider-all';
|
|
2
|
+
|
|
1
3
|
export const HUB_LAST_CONNECTED_WALLETS = 'hub-v1-last-connected-wallets';
|
|
4
|
+
|
|
5
|
+
// Wallets that always report themselves installed (hardware wallets and connection protocols).
|
|
6
|
+
export const WALLET_DETECTED_SKIPPED_IDS: string[] = [
|
|
7
|
+
WalletTypes.LEDGER,
|
|
8
|
+
WalletTypes.TREZOR,
|
|
9
|
+
WalletTypes.LEDGER_WALLET,
|
|
10
|
+
WalletTypes.WALLET_CONNECT_2,
|
|
11
|
+
WalletTypes.TON_CONNECT,
|
|
12
|
+
WalletTypes.DEFAULT,
|
|
13
|
+
];
|
package/src/hub/errors.test.ts
CHANGED
|
@@ -1,8 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
CONNECTION_ERROR_MESSAGES,
|
|
3
|
-
ConnectionErrorType,
|
|
4
|
-
WalletConnectionError,
|
|
5
|
-
} from '@hub3js/std/utils';
|
|
1
|
+
import { Hub3Error } from '@hub3js/core';
|
|
6
2
|
import { error as logError } from '@rango-dev/logging-core';
|
|
7
3
|
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
8
4
|
|
|
@@ -18,26 +14,27 @@ describe('reportConnectionFailure', () => {
|
|
|
18
14
|
namespace: 'EVM',
|
|
19
15
|
origin: 'manual',
|
|
20
16
|
} as const;
|
|
17
|
+
const context = {
|
|
18
|
+
walletType: 'metamask',
|
|
19
|
+
namespace: 'EVM',
|
|
20
|
+
origin: 'manual',
|
|
21
|
+
};
|
|
21
22
|
|
|
22
23
|
beforeEach(() => {
|
|
23
24
|
vi.mocked(logError).mockClear();
|
|
24
25
|
});
|
|
25
26
|
|
|
26
|
-
it.each([
|
|
27
|
-
'drop a %s
|
|
27
|
+
it.each(['PROVIDER_USER_REJECTED_REQUEST', 'PROVIDER_LOCKED'] as const)(
|
|
28
|
+
'drop a %s error',
|
|
28
29
|
(type) => {
|
|
29
|
-
reportConnectionFailure(
|
|
30
|
-
new WalletConnectionError(CONNECTION_ERROR_MESSAGES[type], { type }),
|
|
31
|
-
options
|
|
32
|
-
);
|
|
30
|
+
reportConnectionFailure(new Hub3Error(type, 'Declined'), options);
|
|
33
31
|
|
|
34
32
|
expect(logError).not.toHaveBeenCalled();
|
|
35
33
|
}
|
|
36
34
|
);
|
|
37
35
|
|
|
38
|
-
it('log
|
|
39
|
-
const error = new
|
|
40
|
-
type: ConnectionErrorType.Unknown,
|
|
36
|
+
it('log a PROVIDER_UNEXPECTED error tagged only with its name, type, wallet, namespace and origin', () => {
|
|
37
|
+
const error = new Hub3Error('PROVIDER_UNEXPECTED', 'Something went wrong', {
|
|
41
38
|
cause: new Error('Something went wrong'),
|
|
42
39
|
});
|
|
43
40
|
|
|
@@ -45,26 +42,50 @@ describe('reportConnectionFailure', () => {
|
|
|
45
42
|
|
|
46
43
|
expect(logError).toHaveBeenCalledOnce();
|
|
47
44
|
expect(logError).toHaveBeenCalledWith(error, {
|
|
48
|
-
tags: {
|
|
49
|
-
name: 'WalletConnectionError',
|
|
50
|
-
type: ConnectionErrorType.Unknown,
|
|
51
|
-
walletType: 'metamask',
|
|
52
|
-
namespace: 'EVM',
|
|
53
|
-
origin: 'manual',
|
|
54
|
-
},
|
|
45
|
+
tags: { name: 'Hub3Error', type: 'PROVIDER_UNEXPECTED', ...context },
|
|
55
46
|
});
|
|
56
47
|
});
|
|
57
48
|
|
|
58
|
-
it('
|
|
59
|
-
|
|
49
|
+
it('log a plain error with its name and no type', () => {
|
|
50
|
+
const error = new TypeError('Something went wrong');
|
|
60
51
|
|
|
61
|
-
|
|
52
|
+
reportConnectionFailure(error, options);
|
|
53
|
+
|
|
54
|
+
expect(logError).toHaveBeenCalledOnce();
|
|
55
|
+
expect(logError).toHaveBeenCalledWith(error, {
|
|
56
|
+
tags: { name: 'TypeError', ...context },
|
|
57
|
+
});
|
|
62
58
|
});
|
|
63
59
|
|
|
64
|
-
it(
|
|
65
|
-
|
|
66
|
-
|
|
60
|
+
it.each([
|
|
61
|
+
['a string', 'Something went wrong', 'Something went wrong'],
|
|
62
|
+
[
|
|
63
|
+
"an object's own message",
|
|
64
|
+
{ code: 4001, message: 'rejected' },
|
|
65
|
+
'rejected',
|
|
66
|
+
],
|
|
67
|
+
[
|
|
68
|
+
'the fixed message for an object without one',
|
|
69
|
+
{ code: 4001 },
|
|
70
|
+
'Non-Error value thrown during connect.',
|
|
71
|
+
],
|
|
72
|
+
[
|
|
73
|
+
'the fixed message for anything else',
|
|
74
|
+
undefined,
|
|
75
|
+
'Non-Error value thrown during connect.',
|
|
76
|
+
],
|
|
77
|
+
])(
|
|
78
|
+
'wrap a non-error as NonErrorThrown and log it, using %s',
|
|
79
|
+
(_, value, message) => {
|
|
80
|
+
reportConnectionFailure(value, options);
|
|
67
81
|
|
|
68
|
-
|
|
69
|
-
|
|
82
|
+
expect(logError).toHaveBeenCalledOnce();
|
|
83
|
+
const [logged, data] = vi.mocked(logError).mock.calls[0];
|
|
84
|
+
expect(logged).toBeInstanceOf(Error);
|
|
85
|
+
expect(logged.name).toBe('NonErrorThrown');
|
|
86
|
+
expect(logged.message).toBe(message);
|
|
87
|
+
expect(logged.cause).toBe(value);
|
|
88
|
+
expect(data).toEqual({ tags: { name: 'NonErrorThrown', ...context } });
|
|
89
|
+
}
|
|
90
|
+
);
|
|
70
91
|
});
|
package/src/hub/errors.ts
CHANGED
|
@@ -1,39 +1,45 @@
|
|
|
1
|
+
import type { WalletConnectOrigin } from '../events.js';
|
|
1
2
|
import type { WalletType } from '@hub3js/core';
|
|
2
3
|
import type { Namespace } from '@hub3js/namespaces';
|
|
3
4
|
|
|
4
|
-
import {
|
|
5
|
+
import { getErrorMessage, Hub3Error } from '@hub3js/core';
|
|
5
6
|
import { error as logError } from '@rango-dev/logging-core';
|
|
6
7
|
|
|
7
|
-
|
|
8
|
+
const NON_ERROR_THROWN_NAME = 'NonErrorThrown';
|
|
9
|
+
const NON_ERROR_THROWN_MESSAGE = 'Non-Error value thrown during connect.';
|
|
8
10
|
|
|
9
|
-
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
function wrapNonError(value: unknown): Error {
|
|
12
|
+
const message =
|
|
13
|
+
typeof value === 'string'
|
|
14
|
+
? value
|
|
15
|
+
: getErrorMessage(value) ?? NON_ERROR_THROWN_MESSAGE;
|
|
16
|
+
const wrapper = new Error(message, { cause: value });
|
|
17
|
+
wrapper.name = NON_ERROR_THROWN_NAME;
|
|
18
|
+
return wrapper;
|
|
19
|
+
}
|
|
14
20
|
|
|
21
|
+
/*
|
|
22
|
+
* An allow list: among `Hub3Error`s only `PROVIDER_UNEXPECTED` is logged, so a type added later
|
|
23
|
+
* (something the user can act on, like a rejection or a locked wallet) stays out of the logs.
|
|
24
|
+
*/
|
|
15
25
|
export function reportConnectionFailure(
|
|
16
26
|
error: unknown,
|
|
17
27
|
options: {
|
|
18
28
|
walletType: WalletType;
|
|
19
29
|
namespace: Namespace;
|
|
20
|
-
origin:
|
|
30
|
+
origin: WalletConnectOrigin;
|
|
21
31
|
}
|
|
22
32
|
): void {
|
|
23
|
-
if (
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
33
|
+
if (error instanceof Hub3Error) {
|
|
34
|
+
if (error.type !== 'PROVIDER_UNEXPECTED') {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
logError(error, {
|
|
38
|
+
tags: { name: error.name, type: error.type, ...options },
|
|
39
|
+
});
|
|
27
40
|
return;
|
|
28
41
|
}
|
|
29
42
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
name: error.name,
|
|
33
|
-
type: error.type,
|
|
34
|
-
walletType: options.walletType,
|
|
35
|
-
namespace: options.namespace,
|
|
36
|
-
origin: options.origin,
|
|
37
|
-
},
|
|
38
|
-
});
|
|
43
|
+
const reported = error instanceof Error ? error : wrapNonError(error);
|
|
44
|
+
logError(reported, { tags: { name: reported.name, ...options } });
|
|
39
45
|
}
|
package/src/hub/helpers.ts
CHANGED
|
@@ -10,7 +10,6 @@ import {
|
|
|
10
10
|
CAIP_LITECOIN_CHAIN_ID,
|
|
11
11
|
CAIP_ZCASH_CHAIN_ID,
|
|
12
12
|
} from '@hub3js/bip122';
|
|
13
|
-
import { ConnectionErrorType, WalletConnectionError } from '@hub3js/std/utils';
|
|
14
13
|
import { CAIP_TRON_CHAIN_ID } from '@hub3js/tron';
|
|
15
14
|
import { CAIP_NAMESPACE as CAIP_TON_NAMESPACE } from '@hub3js/tvm';
|
|
16
15
|
import { formatAddressWithNetwork } from '@rango-dev/internal-blockchains';
|
|
@@ -182,19 +181,6 @@ export function createQueue(options?: {
|
|
|
182
181
|
return queueTask;
|
|
183
182
|
}
|
|
184
183
|
|
|
185
|
-
// Once the user rejects one of a wallet's namespaces, its remaining queued namespaces are abandoned.
|
|
186
|
-
export function removeQueuedNamespacesOnRejection(
|
|
187
|
-
error: unknown,
|
|
188
|
-
actions: { removeCurrentKeyFromQueue: () => void }
|
|
189
|
-
) {
|
|
190
|
-
if (
|
|
191
|
-
error instanceof WalletConnectionError &&
|
|
192
|
-
error.type === ConnectionErrorType.Rejected
|
|
193
|
-
) {
|
|
194
|
-
actions.removeCurrentKeyFromQueue();
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
|
|
198
184
|
export function shouldTryAutoConnect(
|
|
199
185
|
props: Pick<ProviderProps, 'allBlockChains' | 'autoConnect'>
|
|
200
186
|
): boolean {
|