@rango-dev/widget-embedded 0.39.1-next.16 → 0.39.1-next.18

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rango-dev/widget-embedded",
3
- "version": "0.39.1-next.16",
3
+ "version": "0.39.1-next.18",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "source": "./src/index.ts",
@@ -54,4 +54,4 @@
54
54
  "publishConfig": {
55
55
  "access": "public"
56
56
  }
57
- }
57
+ }
@@ -0,0 +1,26 @@
1
+ import type { OnUpdateState } from './Wallets.types';
2
+ import type { LegacyEventHandler } from '@rango-dev/wallets-core/legacy';
3
+
4
+ import { LegacyEvents } from '@rango-dev/wallets-core/legacy';
5
+
6
+ /*
7
+ * propagate updates for Dapps using external wallets
8
+ *
9
+ * Note: to take more control over the public interface and wallets-core interface (which may be changed) we use this layer
10
+ */
11
+ export function propagateEvents(
12
+ cb: OnUpdateState,
13
+ eventParams: Parameters<LegacyEventHandler>
14
+ ): void {
15
+ const [walletType, event, value, coreState, info] = eventParams;
16
+
17
+ /*
18
+ * PROVIDER_DISCONNECTED has conflict with WalletEventTypes.DISCONNECT since they are doing samething, the first one is using only for Hub, the second one is what we exposed to the lib users.
19
+ * so for backward-compat we need to keep the behavior of WalletEventTypes.DISCONNECT
20
+ */
21
+ if (event === LegacyEvents.PROVIDER_DISCONNECTED) {
22
+ return;
23
+ }
24
+
25
+ cb(walletType, event, value, coreState, info);
26
+ }
@@ -4,25 +4,18 @@ import type {
4
4
  WidgetContextInterface,
5
5
  } from './Wallets.types';
6
6
  import type { ProvidersOptions } from '../../utils/providers';
7
- import type { LegacyEventHandler as EventHandler } from '@rango-dev/wallets-core/legacy';
7
+ import type { LegacyEventHandler } from '@rango-dev/wallets-core/dist/legacy/mod';
8
8
  import type { PropsWithChildren } from 'react';
9
9
 
10
- import {
11
- legacyFormatAddressWithNetwork as formatAddressWithNetwork,
12
- legacyReadAccountAddress as readAccountAddress,
13
- } from '@rango-dev/wallets-core/legacy';
14
- import { Events, Provider } from '@rango-dev/wallets-react';
15
- import { type Network } from '@rango-dev/wallets-shared';
16
- import { isEvmBlockchain } from 'rango-sdk';
10
+ import { Provider } from '@rango-dev/wallets-react';
17
11
  import React, { createContext, useEffect, useMemo, useRef } from 'react';
18
12
 
19
13
  import { useWalletProviders } from '../../hooks/useWalletProviders';
20
14
  import { AppStoreProvider, useAppStore } from '../../store/AppStore';
21
15
  import { useUiStore } from '../../store/ui';
22
- import {
23
- prepareAccountsForWalletStore,
24
- walletAndSupportedChainsNames,
25
- } from '../../utils/wallets';
16
+
17
+ import { useUpdates } from './useUpdates';
18
+ import { propagateEvents } from './Wallets.helpers';
26
19
 
27
20
  export const WidgetContext = createContext<WidgetContextInterface>({
28
21
  onConnectWallet: () => {
@@ -39,11 +32,8 @@ function Main(props: PropsWithChildren<PropTypes>) {
39
32
  updateSettings,
40
33
  fetch: fetchMeta,
41
34
  fetchStatus,
42
- removeBalancesForWallet,
43
35
  } = useAppStore();
44
36
  const blockchains = useAppStore().blockchains();
45
- const { newWalletConnected, disconnectWallet, connectedWallets } =
46
- useAppStore();
47
37
  const config = useAppStore().config;
48
38
 
49
39
  const walletOptions: ProvidersOptions = {
@@ -57,6 +47,10 @@ function Main(props: PropsWithChildren<PropTypes>) {
57
47
  const { providers } = useWalletProviders(config.wallets, walletOptions);
58
48
  const onConnectWalletHandler = useRef<OnWalletConnectionChange>();
59
49
  const onDisconnectWalletHandler = useRef<OnWalletConnectionChange>();
50
+ const { handler: handleEvent } = useUpdates({
51
+ onConnectWalletHandler,
52
+ onDisconnectWalletHandler,
53
+ });
60
54
 
61
55
  useEffect(() => {
62
56
  void fetchMeta().catch(console.log);
@@ -73,131 +67,6 @@ function Main(props: PropsWithChildren<PropTypes>) {
73
67
  }
74
68
  }, [props.config, fetchStatus]);
75
69
 
76
- const evmBasedChainNames = blockchains
77
- .filter(isEvmBlockchain)
78
- .map((chain) => chain.name);
79
-
80
- const onUpdateState: EventHandler = (type, event, value, state, info) => {
81
- if (event === Events.NAMESPACE_DISCONNECTED) {
82
- removeBalancesForWallet(type, {
83
- namespaces: value,
84
- });
85
- }
86
-
87
- if (event === Events.ACCOUNTS) {
88
- const supportedChainNames: Network[] | null =
89
- walletAndSupportedChainsNames(info.supportedBlockchains);
90
-
91
- /*
92
- * When a wallet is connecting to an evm account, we will consider it as the account exists on other evm-compatible blockchains
93
- * To get their balances.
94
- *
95
- * The logic here is for handling switching account functionality in wallets. when a wallet is switching to another account
96
- * we need to clean the balances for old accounts.
97
- *
98
- * Note: hub will do the cleanup on namespace diconnected event.
99
- */
100
- if (!info.isHub) {
101
- const evmAccounts: string[] = [];
102
- const nonEvmAccounts: string[] = [];
103
-
104
- value?.forEach((account: string) => {
105
- const { network } = readAccountAddress(account);
106
- if (evmBasedChainNames.includes(network)) {
107
- evmAccounts.push(account);
108
- } else {
109
- nonEvmAccounts.push(account);
110
- }
111
- });
112
-
113
- const previousAccounts = connectedWallets
114
- .filter((wallet) => wallet.walletType === type)
115
- .map((wallet) =>
116
- formatAddressWithNetwork(wallet.address, wallet.chain)
117
- );
118
-
119
- if (previousAccounts.length > 0) {
120
- if (evmAccounts.length > 0) {
121
- // We use same logic for removing as we use for adding.
122
- const data = prepareAccountsForWalletStore(
123
- type,
124
- evmAccounts,
125
- evmBasedChainNames,
126
- supportedChainNames,
127
- info.isContractWallet
128
- );
129
-
130
- removeBalancesForWallet(type, {
131
- chains: data.map((account) => account.chain),
132
- });
133
- }
134
-
135
- if (nonEvmAccounts.length > 0) {
136
- removeBalancesForWallet(type, {
137
- chains: nonEvmAccounts.map((account) => {
138
- const { network } = readAccountAddress(account);
139
- return network;
140
- }),
141
- });
142
- }
143
- }
144
- }
145
-
146
- // After cleaning up balances, it's time to add new accounts.
147
- if (value) {
148
- const data = prepareAccountsForWalletStore(
149
- type,
150
- value,
151
- evmBasedChainNames,
152
- supportedChainNames,
153
- info.isContractWallet
154
- );
155
- if (data.length) {
156
- void newWalletConnected(data, info.namespace);
157
- }
158
- } else {
159
- disconnectWallet(type);
160
- if (!!onDisconnectWalletHandler.current) {
161
- onDisconnectWalletHandler.current(type);
162
- } else {
163
- console.warn(
164
- `onDisconnectWallet handler hasn't been set. Are you sure?`
165
- );
166
- }
167
- }
168
- }
169
-
170
- /*
171
- * TODO: not sure why we used `Events.Acccounts` for detecting if a provider gets connected or not, if we can use `CONNCECTED` for the legacy
172
- * we can only rely Events.Connected and remove the legacy conidition
173
- */
174
- const isLegacyProviderConnected =
175
- event === Events.ACCOUNTS && state.connected;
176
- const isHubPorviderConnected = event === Events.CONNECTED && info.isHub;
177
- if (isLegacyProviderConnected || isHubPorviderConnected) {
178
- const key = `${type}-${state.network}-${value}`;
179
-
180
- if (!!onConnectWalletHandler.current) {
181
- onConnectWalletHandler.current(key);
182
- } else {
183
- console.warn(`onConnectWallet handler hasn't been set. Are you sure?`);
184
- }
185
- }
186
-
187
- if (event === Events.NETWORK && state.network) {
188
- const key = `${type}-${state.network}`;
189
- if (!!onConnectWalletHandler.current) {
190
- onConnectWalletHandler.current(key);
191
- } else {
192
- console.warn(`onConnectWallet handler hasn't been set. Are you sure?`);
193
- }
194
- }
195
-
196
- // propagate updates for Dapps using external wallets
197
- if (props.onUpdateState) {
198
- props.onUpdateState(type, event, value, state, info);
199
- }
200
- };
201
70
  const isActiveTab = useUiStore.use.isActiveTab();
202
71
 
203
72
  const handlers = useMemo(
@@ -217,7 +86,20 @@ function Main(props: PropsWithChildren<PropTypes>) {
217
86
  <Provider
218
87
  allBlockChains={blockchains}
219
88
  providers={providers}
220
- onUpdateState={onUpdateState}
89
+ onUpdateState={(type, event, value, state, info) => {
90
+ const eventParams: Parameters<LegacyEventHandler> = [
91
+ type,
92
+ event,
93
+ value,
94
+ state,
95
+ info,
96
+ ];
97
+ handleEvent(...eventParams);
98
+
99
+ if (props.onUpdateState) {
100
+ propagateEvents(props.onUpdateState, eventParams);
101
+ }
102
+ }}
221
103
  autoConnect={!!isActiveTab}
222
104
  configs={{
223
105
  wallets: config.wallets,
@@ -1,5 +1,8 @@
1
1
  import type { WidgetConfig } from '../../types';
2
- import type { LegacyEventHandler as EventHandler } from '@rango-dev/wallets-core/legacy';
2
+ import type {
3
+ LegacyEventHandler as EventHandler,
4
+ LegacyEvents,
5
+ } from '@rango-dev/wallets-core/legacy';
3
6
 
4
7
  export type OnWalletConnectionChange = (key: string) => void;
5
8
  export interface WidgetContextInterface {
@@ -17,7 +20,18 @@ export interface WidgetContextInterface {
17
20
  onDisconnectWallet(handler: OnWalletConnectionChange): void;
18
21
  }
19
22
 
23
+ type EventHandlerParams = Parameters<EventHandler>;
24
+ type EventParam = Exclude<LegacyEvents, LegacyEvents.PROVIDER_DISCONNECTED>;
25
+
26
+ export type OnUpdateState = (
27
+ type: EventHandlerParams[0],
28
+ event: EventParam,
29
+ value: EventHandlerParams[2],
30
+ coreState: EventHandlerParams[3],
31
+ info: EventHandlerParams[4]
32
+ ) => void;
33
+
20
34
  export interface PropTypes {
21
- onUpdateState?: EventHandler;
35
+ onUpdateState?: OnUpdateState;
22
36
  config: WidgetConfig;
23
37
  }
@@ -0,0 +1,240 @@
1
+ import type { OnWalletConnectionChange } from './Wallets.types';
2
+ import type {
3
+ LegacyEventHandler as EventHandler,
4
+ LegacyEventHandler,
5
+ } from '@rango-dev/wallets-core/legacy';
6
+
7
+ import {
8
+ legacyFormatAddressWithNetwork as formatAddressWithNetwork,
9
+ legacyReadAccountAddress as readAccountAddress,
10
+ } from '@rango-dev/wallets-core/legacy';
11
+ import { Events } from '@rango-dev/wallets-react';
12
+ import { type Network } from '@rango-dev/wallets-shared';
13
+ import { isEvmBlockchain } from 'rango-sdk';
14
+
15
+ import { useAppStore } from '../../store/AppStore';
16
+ import {
17
+ prepareAccountsForWalletStore,
18
+ walletAndSupportedChainsNames,
19
+ } from '../../utils/wallets';
20
+
21
+ interface UseUpdatesParams {
22
+ onConnectWalletHandler: React.MutableRefObject<
23
+ OnWalletConnectionChange | undefined
24
+ >;
25
+ onDisconnectWalletHandler: React.MutableRefObject<
26
+ OnWalletConnectionChange | undefined
27
+ >;
28
+ }
29
+ interface UseUpdates {
30
+ handler: EventHandler;
31
+ }
32
+
33
+ export function useUpdates(params: UseUpdatesParams): UseUpdates {
34
+ const {
35
+ newWalletConnected,
36
+ disconnectWallet,
37
+ disconnectNamespaces,
38
+ connectedWallets,
39
+ removeBalancesForWallet,
40
+ blockchains,
41
+ } = useAppStore();
42
+
43
+ const { onConnectWalletHandler, onDisconnectWalletHandler } = params;
44
+ const evmBasedChainNames = blockchains()
45
+ .filter(isEvmBlockchain)
46
+ .map((chain) => chain.name);
47
+
48
+ const onAccountsEvent = (
49
+ event: Parameters<LegacyEventHandler>,
50
+ params: {
51
+ supportedChainNames: Network[] | null;
52
+ }
53
+ ) => {
54
+ const [type, , value, , info] = event;
55
+
56
+ const data = prepareAccountsForWalletStore(
57
+ type,
58
+ value,
59
+ evmBasedChainNames,
60
+ params.supportedChainNames,
61
+ info.isContractWallet
62
+ );
63
+
64
+ if (data.length) {
65
+ void newWalletConnected(data, info.namespace);
66
+ }
67
+ };
68
+
69
+ const handleUpdatesForHub: EventHandler = (
70
+ type,
71
+ event,
72
+ value,
73
+ state,
74
+ info
75
+ ) => {
76
+ if (event === Events.ACCOUNTS) {
77
+ const supportedChainNames: Network[] | null =
78
+ walletAndSupportedChainsNames(info.supportedBlockchains);
79
+
80
+ // After cleaning up balances, it's time to add new accounts.
81
+ if (value) {
82
+ onAccountsEvent([type, event, value, state, info], {
83
+ supportedChainNames,
84
+ });
85
+ }
86
+ }
87
+
88
+ if (event === Events.CONNECTED) {
89
+ const key = `${type}-${state.network}-${value}`;
90
+
91
+ if (!!onConnectWalletHandler.current) {
92
+ onConnectWalletHandler.current(key);
93
+ } else {
94
+ console.warn(`onConnectWallet handler hasn't been set. Are you sure?`);
95
+ }
96
+ }
97
+
98
+ if (event === Events.PROVIDER_DISCONNECTED) {
99
+ disconnectWallet(type);
100
+ if (!!onDisconnectWalletHandler.current) {
101
+ onDisconnectWalletHandler.current(type);
102
+ } else {
103
+ console.warn(
104
+ `onDisconnectWallet handler hasn't been set. Are you sure?`
105
+ );
106
+ }
107
+ }
108
+
109
+ if (event === Events.NAMESPACE_DISCONNECTED) {
110
+ disconnectNamespaces(type, value);
111
+ }
112
+ };
113
+
114
+ const handleUpdatesForLegacy: EventHandler = (
115
+ type,
116
+ event,
117
+ value,
118
+ state,
119
+ info
120
+ ) => {
121
+ if (event === Events.ACCOUNTS) {
122
+ const supportedChainNames: Network[] | null =
123
+ walletAndSupportedChainsNames(info.supportedBlockchains);
124
+
125
+ /*
126
+ * When a wallet is connecting to an evm account, we will consider it as the account exists on other evm-compatible blockchains
127
+ * To get their balances.
128
+ *
129
+ * The logic here is for handling switching account functionality in wallets. when a wallet is switching to another account
130
+ * we need to clean the balances for old accounts.
131
+ *
132
+ * Note: hub will do the cleanup on namespace diconnected event.
133
+ */
134
+ const evmAccounts: string[] = [];
135
+ const nonEvmAccounts: string[] = [];
136
+
137
+ value?.forEach((account: string) => {
138
+ const { network } = readAccountAddress(account);
139
+ if (evmBasedChainNames.includes(network)) {
140
+ evmAccounts.push(account);
141
+ } else {
142
+ nonEvmAccounts.push(account);
143
+ }
144
+ });
145
+
146
+ const previousAccounts = connectedWallets
147
+ .filter((wallet) => wallet.walletType === type)
148
+ .map((wallet) =>
149
+ formatAddressWithNetwork(wallet.address, wallet.chain)
150
+ );
151
+
152
+ if (previousAccounts.length > 0) {
153
+ if (evmAccounts.length > 0) {
154
+ // We use same logic for removing as we use for adding.
155
+ const data = prepareAccountsForWalletStore(
156
+ type,
157
+ evmAccounts,
158
+ evmBasedChainNames,
159
+ supportedChainNames,
160
+ info.isContractWallet
161
+ );
162
+
163
+ removeBalancesForWallet(type, {
164
+ chains: data.map((account) => account.chain),
165
+ });
166
+ }
167
+
168
+ if (nonEvmAccounts.length > 0) {
169
+ removeBalancesForWallet(type, {
170
+ chains: nonEvmAccounts.map((account) => {
171
+ const { network } = readAccountAddress(account);
172
+ return network;
173
+ }),
174
+ });
175
+ }
176
+ }
177
+
178
+ // After cleaning up balances, it's time to add new accounts.
179
+ if (value) {
180
+ onAccountsEvent([type, event, value, state, info], {
181
+ supportedChainNames,
182
+ });
183
+ } else {
184
+ disconnectWallet(type);
185
+ if (!!onDisconnectWalletHandler.current) {
186
+ onDisconnectWalletHandler.current(type);
187
+ } else {
188
+ console.warn(
189
+ `onDisconnectWallet handler hasn't been set. Are you sure?`
190
+ );
191
+ }
192
+ }
193
+ }
194
+
195
+ /*
196
+ * TODO: not sure why we used `Events.Acccounts` for detecting if a provider gets connected or not, if we can use `CONNCECTED` for the legacy
197
+ * we can only rely Events.Connected and remove the legacy conidition
198
+ */
199
+ if (event === Events.ACCOUNTS && state.connected) {
200
+ const key = `${type}-${state.network}-${value}`;
201
+
202
+ if (!!onConnectWalletHandler.current) {
203
+ onConnectWalletHandler.current(key);
204
+ } else {
205
+ console.warn(`onConnectWallet handler hasn't been set. Are you sure?`);
206
+ }
207
+ }
208
+ };
209
+
210
+ const handleUpdatesForBoth: EventHandler = (
211
+ type,
212
+ event,
213
+ _value,
214
+ state,
215
+ _info
216
+ ) => {
217
+ if (event === Events.NETWORK && state.network) {
218
+ const key = `${type}-${state.network}`;
219
+ if (!!onConnectWalletHandler.current) {
220
+ onConnectWalletHandler.current(key);
221
+ } else {
222
+ console.warn(`onConnectWallet handler hasn't been set. Are you sure?`);
223
+ }
224
+ }
225
+ };
226
+
227
+ const handler: EventHandler = (type, event, value, state, info) => {
228
+ if (info.isHub) {
229
+ handleUpdatesForHub(type, event, value, state, info);
230
+ } else {
231
+ handleUpdatesForLegacy(type, event, value, state, info);
232
+ }
233
+
234
+ handleUpdatesForBoth(type, event, value, state, info);
235
+ };
236
+
237
+ return {
238
+ handler,
239
+ };
240
+ }
@@ -0,0 +1,19 @@
1
+ import type { StateCreator } from 'zustand';
2
+
3
+ /**
4
+ * you can wrap your slice in this middleware, it will update `lastUpdatedAt` for that slice when a zustand's `set` is calling
5
+ */
6
+ export const keepLastUpdated: <Store, Slice extends { lastUpdatedAt: number }>(
7
+ config: StateCreator<Store, [], [], Slice>
8
+ ) => StateCreator<Store, [], [], Slice> = (slice) => (set, get, api) => {
9
+ const modifedSet: typeof set = (...params) => {
10
+ set((state) => {
11
+ return {
12
+ ...state,
13
+ lastUpdatedAt: +new Date(),
14
+ };
15
+ });
16
+ set(...params);
17
+ };
18
+ return slice(modifedSet, get, api);
19
+ };