@rango-dev/wallets-react 0.49.2-next.3 → 0.49.2-next.5

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.
@@ -7,14 +7,34 @@ 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 { runSequentiallyWithoutFailure } from './helpers.js';
12
19
  import { LastConnectedWalletsFromStorage } from './lastConnectedWallets.js';
13
20
  import {
14
21
  convertNamespaceNetworkToEvmChainId,
15
22
  isEvmNamespace,
23
+ transformHubResultToLegacyResult,
16
24
  } from './utils.js';
17
25
 
26
+ function createAutoConnectEventPayload(
27
+ walletName: WalletType,
28
+ namespaceInput: NamespaceInputForConnect
29
+ ) {
30
+ return {
31
+ walletName,
32
+ namespace: namespaceInput.namespace,
33
+ chain: namespaceInput.network || null,
34
+ origin: 'auto' as const,
35
+ };
36
+ }
37
+
18
38
  function isEvmNamespaceInput(
19
39
  namespace: NamespaceInputForConnect
20
40
  ): namespace is NamespaceInputForConnect<'EVM'> {
@@ -35,9 +55,10 @@ async function eagerConnect(
35
55
  params: {
36
56
  getHub: () => Hub;
37
57
  allBlockChains: UseAdapterParams['allBlockChains'];
58
+ emitter?: WalletEventEmitter;
38
59
  }
39
60
  ) {
40
- const { getHub, allBlockChains } = params;
61
+ const { getHub, allBlockChains, emitter } = params;
41
62
  const wallet = getHub().get(type);
42
63
  if (!wallet) {
43
64
  throw new Error(
@@ -81,10 +102,25 @@ async function eagerConnect(
81
102
  } else {
82
103
  connectNamespacePromise = async () => namespace.connect();
83
104
  }
105
+ const eventPayload = createAutoConnectEventPayload(type, info);
84
106
  try {
85
- await connectNamespacePromise();
107
+ const result = await connectNamespacePromise();
108
+ emitter?.emit(WalletEventChannel, {
109
+ type: WalletEventTypes.WALLET_CONNECTED,
110
+ payload: {
111
+ ...eventPayload,
112
+ chain: toBlockchainName(
113
+ transformHubResultToLegacyResult(result).network,
114
+ allBlockChains
115
+ ),
116
+ },
117
+ });
86
118
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
87
119
  } catch (e: any) {
120
+ emitter?.emit(WalletEventChannel, {
121
+ type: WalletEventTypes.WALLET_CONNECT_FAILED,
122
+ payload: eventPayload,
123
+ });
88
124
  /*
89
125
  * Since we check for connect failures using `instanceof Error`
90
126
  * this check is added here to make sure the thrown error always is an instance of `Error`
@@ -182,8 +218,9 @@ export async function autoConnect(deps: {
182
218
  getHub: () => Hub;
183
219
  allBlockChains: UseAdapterParams['allBlockChains'];
184
220
  wallets?: (WalletType | Provider)[];
221
+ emitter?: WalletEventEmitter;
185
222
  }): Promise<void> {
186
- const { getHub, allBlockChains, wallets } = deps;
223
+ const { getHub, allBlockChains, wallets, emitter } = deps;
187
224
  const lastConnectedWallets = lastConnectedWalletsFromStorage.list();
188
225
  const walletIds = Object.keys(lastConnectedWallets);
189
226
 
@@ -219,9 +256,23 @@ export async function autoConnect(deps: {
219
256
  return;
220
257
  }
221
258
 
259
+ lastConnectedNamespaces.forEach((namespace) => {
260
+ emitter?.emit(WalletEventChannel, {
261
+ type: WalletEventTypes.WALLET_CONNECT_INITIATED,
262
+ payload: createAutoConnectEventPayload(providerName, namespace),
263
+ });
264
+ });
265
+
222
266
  const { successNamespaces, failedNamespaces } =
223
267
  await tryRunCanEagerConnect(lastConnectedNamespaces, wallet);
224
268
 
269
+ failedNamespaces.forEach((namespace) => {
270
+ emitter?.emit(WalletEventChannel, {
271
+ type: WalletEventTypes.WALLET_CONNECT_FAILED,
272
+ payload: createAutoConnectEventPayload(providerName, namespace),
273
+ });
274
+ });
275
+
225
276
  if (!successNamespaces.length) {
226
277
  walletsToRemoveFromPersistence.push(providerName);
227
278
  return;
@@ -235,6 +286,7 @@ export async function autoConnect(deps: {
235
286
  eagerConnect(providerName, successNamespaces, {
236
287
  allBlockChains,
237
288
  getHub,
289
+ emitter,
238
290
  }).catch((error) => console.warn(error))
239
291
  );
240
292
  });
@@ -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
+ ];
@@ -1,11 +1,13 @@
1
1
  import type { AllProxiedNamespaces, ExtensionLink } from './types.js';
2
2
  import type {
3
3
  ConnectResult,
4
+ EventHandler,
4
5
  NamespaceInputForConnect,
5
6
  WalletInfo,
6
7
  } from '../legacy/mod.js';
7
8
  import type { ProviderContext, ProviderProps } from '../types.js';
8
9
  import type { Provider, WalletType } from '@hub3js/core';
10
+ import type { Event } from '@hub3js/core/store';
9
11
  import type { Accounts, AccountsWithActiveChain } from '@hub3js/std/types';
10
12
 
11
13
  import { utils } from '@hub3js/evm';
@@ -16,7 +18,14 @@ import {
16
18
  import { useEffect, useRef, useState } from 'react';
17
19
  import { Ok, Result } from 'ts-results';
18
20
 
21
+ import {
22
+ emitWalletDetectedOnce,
23
+ toBlockchainName,
24
+ WalletEventChannel,
25
+ WalletEventTypes,
26
+ } from '../events.js';
19
27
  import { withErrorLoggingApi } from '../helpers.js';
28
+ import { Events } from '../legacy/mod.js';
20
29
 
21
30
  import { autoConnect } from './autoConnect.js';
22
31
  import { HUB_LAST_CONNECTED_WALLETS } from './constants.js';
@@ -45,6 +54,7 @@ export function useHubAdapter(params: UseAdapterParams): ProviderContext {
45
54
  const dataRef = useRef({
46
55
  onUpdateState: params.onUpdateState,
47
56
  allBlockChains: params.allBlockChains,
57
+ emitter: params.emitter,
48
58
  });
49
59
  const hubInitiated = useRef(false);
50
60
 
@@ -75,30 +85,31 @@ export function useHubAdapter(params: UseAdapterParams): ProviderContext {
75
85
  dataRef.current = {
76
86
  onUpdateState: params.onUpdateState,
77
87
  allBlockChains: params.allBlockChains,
88
+ emitter: params.emitter,
78
89
  };
79
90
  }, [params]);
80
91
 
81
92
  useEffect(() => {
82
- getStore()
83
- .subscribe((event) => {
84
- if (dataRef.current.onUpdateState) {
85
- try {
86
- mapHubEventsToLegacy(
87
- getHub(),
88
- event,
89
- dataRef.current.onUpdateState,
90
- {
91
- allBlockChains: dataRef.current.allBlockChains,
92
- lastConnectAttemptParams: lastConnectAttemptParamsRef.current,
93
- }
94
- );
95
- } catch (e) {
96
- console.error(e);
97
- }
98
- }
99
- rerender((currentRender) => currentRender + 1);
100
- })
101
- .flushEvents();
93
+ const onUpdateState: EventHandler = (type, event, value, state, info) => {
94
+ if (event === Events.INSTALLED && state.installed) {
95
+ emitWalletDetectedOnce(type, dataRef.current.emitter);
96
+ }
97
+ dataRef.current.onUpdateState?.(type, event, value, state, info);
98
+ };
99
+
100
+ const onStoreEvent = (event: Event) => {
101
+ try {
102
+ mapHubEventsToLegacy(getHub(), event, onUpdateState, {
103
+ allBlockChains: dataRef.current.allBlockChains,
104
+ lastConnectAttemptParams: lastConnectAttemptParamsRef.current,
105
+ });
106
+ } catch (e) {
107
+ console.error(e);
108
+ }
109
+ rerender((currentRender) => currentRender + 1);
110
+ };
111
+
112
+ getStore().subscribe(onStoreEvent).flushEvents();
102
113
  }, []);
103
114
 
104
115
  // Initialize hub only after blockchain meta is available (e.g. WalletConnect needs it at init).
@@ -120,6 +131,7 @@ export function useHubAdapter(params: UseAdapterParams): ProviderContext {
120
131
  allBlockChains: params.allBlockChains,
121
132
  getHub,
122
133
  wallets: params.configs?.wallets,
134
+ emitter: dataRef.current.emitter,
123
135
  });
124
136
  },
125
137
  });
@@ -258,7 +270,46 @@ export function useHubAdapter(params: UseAdapterParams): ProviderContext {
258
270
  },
259
271
  };
260
272
  });
261
- return queueTask(connectNamespaceProcess, type);
273
+
274
+ // Reconnecting an already connected namespace (e.g. only to switch its network) isn't reported.
275
+ const connectNamespaceProcessWithEvents = async () => {
276
+ if (namespace.state()[0]().connected) {
277
+ return connectNamespaceProcess();
278
+ }
279
+
280
+ const eventPayload = {
281
+ walletName: type,
282
+ namespace: namespaceInput.namespace,
283
+ origin: 'manual' as const,
284
+ };
285
+ const requestedChain = namespaceInput.network || null;
286
+
287
+ dataRef.current.emitter?.emit(WalletEventChannel, {
288
+ type: WalletEventTypes.WALLET_CONNECT_INITIATED,
289
+ payload: { ...eventPayload, chain: requestedChain },
290
+ });
291
+ try {
292
+ const result = await connectNamespaceProcess();
293
+ dataRef.current.emitter?.emit(WalletEventChannel, {
294
+ type: WalletEventTypes.WALLET_CONNECTED,
295
+ payload: {
296
+ ...eventPayload,
297
+ chain: toBlockchainName(
298
+ result.response.network,
299
+ params.allBlockChains
300
+ ),
301
+ },
302
+ });
303
+ return result;
304
+ } catch (error) {
305
+ dataRef.current.emitter?.emit(WalletEventChannel, {
306
+ type: WalletEventTypes.WALLET_CONNECT_FAILED,
307
+ payload: { ...eventPayload, chain: requestedChain },
308
+ });
309
+ throw error;
310
+ }
311
+ };
312
+ return queueTask(connectNamespaceProcessWithEvents, type);
262
313
  }
263
314
  );
264
315
 
@@ -312,6 +363,10 @@ export function useHubAdapter(params: UseAdapterParams): ProviderContext {
312
363
  !namespaces || namespaces.includes(namespace.namespaceId);
313
364
  const namespaceIsConnected = namespace.state()[0]().connected;
314
365
  if (namespaceShouldBeDisconnected && namespaceIsConnected) {
366
+ dataRef.current.emitter?.emit(WalletEventChannel, {
367
+ type: WalletEventTypes.WALLET_DISCONNECTED,
368
+ payload: { walletName: type, namespace: namespace.namespaceId },
369
+ });
315
370
  return namespace.disconnect();
316
371
  }
317
372
  });
package/src/index.ts CHANGED
@@ -1,4 +1,16 @@
1
1
  export { default as Provider } from './provider.js';
2
2
  export { useWallets } from './hooks.js';
3
3
  export type * from './types.js';
4
+ export { WalletEventChannel, WalletEventTypes } from './events.js';
5
+ export type {
6
+ Emitter,
7
+ WalletConnectedEventPayload,
8
+ WalletConnectFailedEventPayload,
9
+ WalletConnectInitiatedEventPayload,
10
+ WalletConnectOrigin,
11
+ WalletDetectedEventPayload,
12
+ WalletDisconnectedEventPayload,
13
+ WalletEventData,
14
+ WalletEvents,
15
+ } from './events.js';
4
16
  export * from './legacy/types.js';
package/src/types.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import type { Emitter, WalletEvents } from './events.js';
1
2
  import type {
2
3
  ConnectResult,
3
4
  EventHandler,
@@ -43,6 +44,8 @@ export type ProviderProps = PropsWithChildren<{
43
44
  allBlockChains?: BlockchainMeta[];
44
45
  autoConnect?: boolean;
45
46
  providers: Provider[];
47
+ /** Receives wallet events on the `walletEvent` channel. */
48
+ emitter?: Emitter<WalletEvents>;
46
49
  configs?: {
47
50
  wallets?: (WalletType | Provider)[];
48
51
  walletOptions?: {