@dynamic-labs-sdk/react-hooks 1.16.1 → 1.17.1

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.
Files changed (66) hide show
  1. package/dist/foundation/useBaseQuery/index.d.ts +1 -1
  2. package/dist/foundation/useBaseQuery/index.d.ts.map +1 -1
  3. package/dist/foundation/useBaseQuery/useBaseQuery.d.ts +29 -12
  4. package/dist/foundation/useBaseQuery/useBaseQuery.d.ts.map +1 -1
  5. package/dist/hooks/wrappers/functions/useAuthenticateTotpMfaDevice/useAuthenticateTotpMfaDevice.d.ts.map +1 -1
  6. package/dist/hooks/wrappers/functions/useCompleteDeviceRegistration/useCompleteDeviceRegistration.d.ts.map +1 -1
  7. package/dist/hooks/wrappers/functions/useDeleteMfaDevice/useDeleteMfaDevice.d.ts.map +1 -1
  8. package/dist/hooks/wrappers/functions/useDeletePasskey/useDeletePasskey.d.ts.map +1 -1
  9. package/dist/hooks/wrappers/functions/useGetActiveNetworkData/useGetActiveNetworkData.d.ts.map +1 -1
  10. package/dist/hooks/wrappers/functions/useGetActiveNetworkId/useGetActiveNetworkId.d.ts.map +1 -1
  11. package/dist/hooks/wrappers/functions/useGetAvailableWalletProvidersData/useGetAvailableWalletProvidersData.d.ts +4 -1
  12. package/dist/hooks/wrappers/functions/useGetAvailableWalletProvidersData/useGetAvailableWalletProvidersData.d.ts.map +1 -1
  13. package/dist/hooks/wrappers/functions/useGetConnectedAddresses/useGetConnectedAddresses.d.ts +4 -1
  14. package/dist/hooks/wrappers/functions/useGetConnectedAddresses/useGetConnectedAddresses.d.ts.map +1 -1
  15. package/dist/hooks/wrappers/functions/useGetGoogleDriveBackupReadiness/useGetGoogleDriveBackupReadiness.d.ts +4 -0
  16. package/dist/hooks/wrappers/functions/useGetGoogleDriveBackupReadiness/useGetGoogleDriveBackupReadiness.d.ts.map +1 -1
  17. package/dist/hooks/wrappers/functions/useGetKrakenAccounts/useGetKrakenAccounts.d.ts +4 -0
  18. package/dist/hooks/wrappers/functions/useGetKrakenAccounts/useGetKrakenAccounts.d.ts.map +1 -1
  19. package/dist/hooks/wrappers/functions/useGetKrakenWhitelistedAddresses/useGetKrakenWhitelistedAddresses.d.ts +4 -0
  20. package/dist/hooks/wrappers/functions/useGetKrakenWhitelistedAddresses/useGetKrakenWhitelistedAddresses.d.ts.map +1 -1
  21. package/dist/hooks/wrappers/functions/useGetMfaDevices/useGetMfaDevices.d.ts +4 -0
  22. package/dist/hooks/wrappers/functions/useGetMfaDevices/useGetMfaDevices.d.ts.map +1 -1
  23. package/dist/hooks/wrappers/functions/useGetMfaMethods/useGetMfaMethods.d.ts +4 -0
  24. package/dist/hooks/wrappers/functions/useGetMfaMethods/useGetMfaMethods.d.ts.map +1 -1
  25. package/dist/hooks/wrappers/functions/useGetMfaRecoveryCodes/useGetMfaRecoveryCodes.d.ts +4 -0
  26. package/dist/hooks/wrappers/functions/useGetMfaRecoveryCodes/useGetMfaRecoveryCodes.d.ts.map +1 -1
  27. package/dist/hooks/wrappers/functions/useGetNativeBalance/useGetNativeBalance.d.ts.map +1 -1
  28. package/dist/hooks/wrappers/functions/useGetPasskeys/useGetPasskeys.d.ts +4 -0
  29. package/dist/hooks/wrappers/functions/useGetPasskeys/useGetPasskeys.d.ts.map +1 -1
  30. package/dist/hooks/wrappers/functions/useGetRegisteredDevices/useGetRegisteredDevices.d.ts +4 -0
  31. package/dist/hooks/wrappers/functions/useGetRegisteredDevices/useGetRegisteredDevices.d.ts.map +1 -1
  32. package/dist/hooks/wrappers/functions/useGetTokenBalances/useGetTokenBalances.d.ts.map +1 -1
  33. package/dist/hooks/wrappers/functions/useGetTransactionHistory/useGetTransactionHistory.d.ts +1 -1
  34. package/dist/hooks/wrappers/functions/useGetTransactionHistory/useGetTransactionHistory.d.ts.map +1 -1
  35. package/dist/hooks/wrappers/functions/useGetWalletOptionsCatalogue/useGetWalletOptionsCatalogue.d.ts +8 -1
  36. package/dist/hooks/wrappers/functions/useGetWalletOptionsCatalogue/useGetWalletOptionsCatalogue.d.ts.map +1 -1
  37. package/dist/hooks/wrappers/functions/useOnWalletProviderEvent/useOnWalletProviderEvent.d.ts +14 -2
  38. package/dist/hooks/wrappers/functions/useOnWalletProviderEvent/useOnWalletProviderEvent.d.ts.map +1 -1
  39. package/dist/hooks/wrappers/functions/useRegisterPasskey/useRegisterPasskey.d.ts.map +1 -1
  40. package/dist/hooks/wrappers/functions/useRegisterTotpMfaDevice/useRegisterTotpMfaDevice.d.ts.map +1 -1
  41. package/dist/hooks/wrappers/functions/useRevokeAllRegisteredDevices/useRevokeAllRegisteredDevices.d.ts.map +1 -1
  42. package/dist/hooks/wrappers/functions/useRevokeRegisteredDevice/useRevokeRegisteredDevice.d.ts.map +1 -1
  43. package/dist/hooks/wrappers/functions/useSetDefaultMfaDevice/useSetDefaultMfaDevice.d.ts.map +1 -1
  44. package/dist/hooks/wrappers/functions/useSwitchActiveNetwork/useSwitchActiveNetwork.d.ts.map +1 -1
  45. package/dist/index.cjs +375 -83
  46. package/dist/index.cjs.map +1 -1
  47. package/dist/index.esm.js +375 -83
  48. package/dist/index.esm.js.map +1 -1
  49. package/dist/middlewares/invalidateOnEvent/index.d.ts +2 -0
  50. package/dist/middlewares/invalidateOnEvent/index.d.ts.map +1 -0
  51. package/dist/middlewares/invalidateOnEvent/invalidateOnEvent.d.ts +29 -0
  52. package/dist/middlewares/invalidateOnEvent/invalidateOnEvent.d.ts.map +1 -0
  53. package/dist/middlewares/invalidateOnUserSwitch/index.d.ts +2 -0
  54. package/dist/middlewares/invalidateOnUserSwitch/index.d.ts.map +1 -0
  55. package/dist/middlewares/invalidateOnUserSwitch/invalidateOnUserSwitch.d.ts +27 -0
  56. package/dist/middlewares/invalidateOnUserSwitch/invalidateOnUserSwitch.d.ts.map +1 -0
  57. package/dist/middlewares/invalidateOnWalletProviderEvent/index.d.ts +2 -0
  58. package/dist/middlewares/invalidateOnWalletProviderEvent/index.d.ts.map +1 -0
  59. package/dist/middlewares/invalidateOnWalletProviderEvent/invalidateOnWalletProviderEvent.d.ts +36 -0
  60. package/dist/middlewares/invalidateOnWalletProviderEvent/invalidateOnWalletProviderEvent.d.ts.map +1 -0
  61. package/dist/middlewares/invalidatesQueries/index.d.ts +2 -0
  62. package/dist/middlewares/invalidatesQueries/index.d.ts.map +1 -0
  63. package/dist/middlewares/invalidatesQueries/invalidatesQueries.d.ts +19 -0
  64. package/dist/middlewares/invalidatesQueries/invalidatesQueries.d.ts.map +1 -0
  65. package/dist/tsconfig.lib.tsbuildinfo +1 -1
  66. package/package.json +4 -4
package/dist/index.esm.js CHANGED
@@ -7,7 +7,7 @@ import { getCore } from "@dynamic-labs-sdk/client/core";
7
7
 
8
8
  //#region package.json
9
9
  var name = "@dynamic-labs-sdk/react-hooks";
10
- var version = "1.16.1";
10
+ var version = "1.17.1";
11
11
 
12
12
  //#endregion
13
13
  //#region src/modules/DynamicContext/DynamicContext.ts
@@ -132,8 +132,8 @@ const QUERY_KEY_PREFIX = [HOOK_KEY_NAMESPACE, "query"];
132
132
  //#endregion
133
133
  //#region src/foundation/useBaseQuery/useBaseQuery.ts
134
134
  /**
135
- * Stable empty default for the `middlewares` param so the common (ungated)
136
- * case never allocates a fresh array per render.
135
+ * Stable empty default for the `middlewares` param so the common case never
136
+ * allocates a fresh array per render.
137
137
  */
138
138
  const EMPTY_MIDDLEWARES = [];
139
139
  /**
@@ -147,23 +147,105 @@ const EMPTY_MIDDLEWARES = [];
147
147
  * so consumers can opt into retries / custom staleTime / etc.
148
148
  * - Callers pass `enabled: false` (typically derived from whether their
149
149
  * pseudo-required arguments are present) to short-circuit fetches.
150
- * - Callers pass `middlewares` (named gating hooks like `requiresUser`) and
151
- * the query only runs when every one returns `true`.
150
+ * - Callers pass `middlewares` (named hooks that run reactive subscriptions
151
+ * such as cache invalidation); each runs on every render.
152
152
  *
153
153
  * Returns the full `useQuery` result — `data`, `error`, `isLoading`,
154
154
  * `isFetching`, `isSuccess`, `refetch`, `status`, etc.
155
155
  */
156
156
  const useBaseQuery = ({ enabled = true, middlewares = EMPTY_MIDDLEWARES, queryFn, queryKey, queryParams }) => {
157
157
  const client = useDynamicClient();
158
- const middlewaresAllow = middlewares.map((useMiddleware) => useMiddleware()).every(Boolean);
158
+ const fullQueryKey = useMemo(() => [...QUERY_KEY_PREFIX, ...queryKey], [queryKey]);
159
+ middlewares.forEach((useMiddleware) => useMiddleware({ queryKey: fullQueryKey }));
159
160
  return useQuery({
160
161
  queryFn: async () => {
161
162
  await waitForClientInitialized(client);
162
163
  return queryFn(client);
163
164
  },
164
- queryKey: useMemo(() => [...QUERY_KEY_PREFIX, ...queryKey], [queryKey]),
165
+ queryKey: fullQueryKey,
165
166
  ...queryParams,
166
- enabled: enabled && middlewaresAllow && queryParams?.enabled !== false
167
+ enabled: enabled && queryParams?.enabled !== false
168
+ });
169
+ };
170
+
171
+ //#endregion
172
+ //#region src/hooks/wrappers/functions/useOnWalletProviderEvent/useOnWalletProviderEvent.ts
173
+ /**
174
+ * Subscribes to an event emitted by a specific wallet provider.
175
+ *
176
+ * Wraps `onWalletProviderEvent` from `@dynamic-labs-sdk/client`. Uses a ref to
177
+ * always call the latest callback without re-subscribing, and cleans up the
178
+ * subscription on unmount or when `event` / `walletProviderKey` change. When
179
+ * `walletProviderKey` is `undefined` the hook subscribes to nothing.
180
+ *
181
+ * @example
182
+ * ```tsx
183
+ * useOnWalletProviderEvent({
184
+ * walletProviderKey: 'metamaskevm',
185
+ * event: 'accountsChanged',
186
+ * callback: ({ addresses }) => console.log(addresses),
187
+ * });
188
+ * ```
189
+ */
190
+ const useOnWalletProviderEvent = ({ callback, event, walletProviderKey }) => {
191
+ const client = useDynamicClient();
192
+ const callbackRef = useRef(callback);
193
+ callbackRef.current = callback;
194
+ useEffect(() => {
195
+ if (walletProviderKey === void 0) return;
196
+ return onWalletProviderEvent({
197
+ callback: ((...args) => {
198
+ callbackRef.current(...args);
199
+ }),
200
+ event,
201
+ walletProviderKey
202
+ }, client);
203
+ }, [
204
+ event,
205
+ walletProviderKey,
206
+ client
207
+ ]);
208
+ };
209
+
210
+ //#endregion
211
+ //#region src/middlewares/invalidateOnWalletProviderEvent/invalidateOnWalletProviderEvent.ts
212
+ /**
213
+ * Query middleware factory that refetches the query whenever the given wallet
214
+ * account's provider emits `event` (e.g. `networkChanged`).
215
+ *
216
+ * It only wires up the subscription, invalidating the query's own cache key so
217
+ * any mounted consumer refetches. This replaces the manual
218
+ * `useOnWalletProviderEvent` + `refetch` boilerplate consumers would otherwise
219
+ * pair with the hook. When `walletAccount` is undefined the subscription is a
220
+ * no-op. The subscription lives here, not in `useBaseQuery`.
221
+ *
222
+ * @example
223
+ * ```ts
224
+ * useBaseQuery({
225
+ * middlewares: [
226
+ * invalidateOnWalletProviderEvent({ event: 'networkChanged', walletAccount }),
227
+ * ],
228
+ * queryFn: (client) => getActiveNetworkData({ walletAccount }, client),
229
+ * queryKey: ['useGetActiveNetworkData', walletAccount],
230
+ * });
231
+ * ```
232
+ */
233
+ const invalidateOnWalletProviderEvent = ({ event, walletAccount }) => (context) => {
234
+ const client = useDynamicClient();
235
+ const queryClient = useQueryClient();
236
+ useOnWalletProviderEvent({
237
+ callback: () => {
238
+ if (!context) return;
239
+ queryClient.invalidateQueries({ queryKey: context.queryKey }).catch((error) => {
240
+ getCore(client).logger.debug("[invalidateOnWalletProviderEvent] failed to invalidate query", {
241
+ error,
242
+ queryKey: context.queryKey,
243
+ stack: error instanceof Error ? error.stack : void 0
244
+ });
245
+ });
246
+ },
247
+ event,
248
+ walletProviderKey: walletAccount?.walletProviderKey
167
249
  });
168
250
  };
169
251
 
@@ -178,6 +260,10 @@ const useBaseQuery = ({ enabled = true, middlewares = EMPTY_MIDDLEWARES, queryFn
178
260
  * ```
179
261
  */
180
262
  const useGetActiveNetworkData = ({ queryParams, ...args }) => useBaseQuery({
263
+ middlewares: [invalidateOnWalletProviderEvent({
264
+ event: "networkChanged",
265
+ walletAccount: args.walletAccount
266
+ })],
181
267
  queryFn: (client) => getActiveNetworkData(args, client),
182
268
  queryKey: ["useGetActiveNetworkData", args],
183
269
  queryParams
@@ -191,6 +277,10 @@ const useGetActiveNetworkData = ({ queryParams, ...args }) => useBaseQuery({
191
277
  * Wraps `getActiveNetworkId` from `@dynamic-labs-sdk/client`.
192
278
  */
193
279
  const useGetActiveNetworkId = ({ queryParams, ...args }) => useBaseQuery({
280
+ middlewares: [invalidateOnWalletProviderEvent({
281
+ event: "networkChanged",
282
+ walletAccount: args.walletAccount
283
+ })],
194
284
  queryFn: (client) => getActiveNetworkId(args, client),
195
285
  queryKey: ["useGetActiveNetworkId", args],
196
286
  queryParams
@@ -302,6 +392,48 @@ const useAuthenticatePasskeyMFA = ({ mutateParams } = {}) => useBaseMutation({
302
392
  mutationFn: ({ client, variables }) => authenticatePasskeyMFA(variables, client)
303
393
  });
304
394
 
395
+ //#endregion
396
+ //#region src/middlewares/invalidatesQueries/invalidatesQueries.ts
397
+ /**
398
+ * Mutation middleware factory that invalidates one or more query hooks when
399
+ * the mutation succeeds, so any mounted consumer of those queries refetches.
400
+ *
401
+ * Pass the query hooks' names (their `queryKey` leaf, e.g. `'useGetMfaDevices'`);
402
+ * each is namespaced under `QUERY_KEY_PREFIX` to match the keys `useBaseQuery`
403
+ * creates. The invalidation loop lives here, not in `useBaseMutation`.
404
+ *
405
+ * @example
406
+ * ```ts
407
+ * useBaseMutation({
408
+ * middlewares: [invalidatesQueries('useGetMfaDevices')],
409
+ * mutationFn: ({ client, variables }) => deleteMfaDevice(variables, client),
410
+ * });
411
+ * ```
412
+ */
413
+ const invalidatesQueries = (...queryKeys) => () => {
414
+ const queryClient = useQueryClient();
415
+ const client = useDynamicClient();
416
+ /**
417
+ * Swallows an `invalidateQueries` rejection so a failed refetch never
418
+ * surfaces as an unhandled rejection nor rejects the combined invalidation
419
+ * promise, while still logging it at debug level so internal devs can see
420
+ * which query failed to refetch. Curried over the query key and extracted
421
+ * to a named handler so the invalidation map stays within the
422
+ * function-nesting limit.
423
+ */
424
+ const debugInvalidationFailure = (queryKey) => (error) => {
425
+ getCore(client).logger.debug("[invalidatesQueries] failed to invalidate query", {
426
+ error,
427
+ queryKey,
428
+ stack: error instanceof Error ? error.stack : void 0
429
+ });
430
+ };
431
+ return { onSuccess: () => Promise.all(queryKeys.map((key) => {
432
+ const queryKey = [...QUERY_KEY_PREFIX, key];
433
+ return queryClient.invalidateQueries({ queryKey }).catch(debugInvalidationFailure(queryKey));
434
+ })) };
435
+ };
436
+
305
437
  //#endregion
306
438
  //#region src/hooks/wrappers/functions/useAuthenticateTotpMfaDevice/useAuthenticateTotpMfaDevice.ts
307
439
  /**
@@ -319,10 +451,13 @@ const useAuthenticatePasskeyMFA = ({ mutateParams } = {}) => useBaseMutation({
319
451
  * </button>
320
452
  * ```
321
453
  */
322
- const useAuthenticateTotpMfaDevice = ({ mutateParams } = {}) => useBaseMutation({
323
- mutateParams,
324
- mutationFn: ({ client, variables }) => authenticateTotpMfaDevice(variables, client)
325
- });
454
+ const useAuthenticateTotpMfaDevice = ({ mutateParams } = {}) => {
455
+ return useBaseMutation({
456
+ middlewares: [invalidatesQueries("useGetMfaDevices")],
457
+ mutateParams,
458
+ mutationFn: ({ client, variables }) => authenticateTotpMfaDevice(variables, client)
459
+ });
460
+ };
326
461
 
327
462
  //#endregion
328
463
  //#region src/foundation/useBaseState/useBaseState.ts
@@ -438,11 +573,17 @@ const useBaseState = ({ event, queryKey, queryParams, selector, defaultValue, sk
438
573
  /**
439
574
  * Returns the available wallet providers and their data.
440
575
  *
441
- * Re-renders when a wallet provider changes (e.g. network switch).
576
+ * Re-renders when the wallet provider registry changes. `walletProviderChanged`
577
+ * fires on register / priority update but NOT on removal, so
578
+ * `walletProviderUnregistered` is wired too — otherwise a removed provider
579
+ * would linger in the returned list.
442
580
  */
443
581
  const useGetAvailableWalletProvidersData = () => useBaseState({
444
582
  defaultValue: [],
445
- event: "walletProviderChanged",
583
+ event: {
584
+ walletProviderChanged: true,
585
+ walletProviderUnregistered: true
586
+ },
446
587
  queryKey: ["useGetAvailableWalletProvidersData"],
447
588
  selector: (client) => getAvailableWalletProvidersData(client)
448
589
  });
@@ -584,6 +725,7 @@ const useAddCoinbaseOnrampOrderEventListener = ({ listener }) => {
584
725
  * ```
585
726
  */
586
727
  const useCompleteDeviceRegistration = ({ mutateParams } = {}) => useBaseMutation({
728
+ middlewares: [invalidatesQueries("useGetRegisteredDevices")],
587
729
  mutateParams,
588
730
  mutationFn: ({ client, variables }) => completeDeviceRegistration(variables, client)
589
731
  });
@@ -651,14 +793,82 @@ const useConnectAndVerifyWithWalletProvider = ({ mutateParams } = {}) => useBase
651
793
  mutationFn: ({ client, variables }) => connectAndVerifyWithWalletProvider(variables, client)
652
794
  });
653
795
 
796
+ //#endregion
797
+ //#region src/hooks/wrappers/functions/useOnEvent/useOnEvent.ts
798
+ /**
799
+ * Subscribes to a Dynamic client event.
800
+ *
801
+ * Uses a ref to always call the latest listener without re-subscribing,
802
+ * and cleans up the subscription on unmount or event name change.
803
+ */
804
+ const useOnEvent = ({ event, listener }) => {
805
+ const client = useDynamicClient();
806
+ const callbackRef = useRef(listener);
807
+ callbackRef.current = listener;
808
+ useEffect(() => onEvent({
809
+ event,
810
+ listener: (...args) => {
811
+ callbackRef.current(...args);
812
+ }
813
+ }, client), [event, client]);
814
+ };
815
+
816
+ //#endregion
817
+ //#region src/middlewares/invalidateOnEvent/invalidateOnEvent.ts
818
+ /**
819
+ * Query middleware factory that refetches the query whenever the given client
820
+ * event fires (e.g. `walletAccountsChanged`).
821
+ *
822
+ * It only wires up the subscription, invalidating the query's own cache key so
823
+ * any mounted consumer refetches. This replaces the manual `useOnEvent` +
824
+ * `refetch` boilerplate consumers would otherwise pair with the hook. It is the
825
+ * client-event counterpart to `invalidateOnWalletProviderEvent` (which listens
826
+ * on a single wallet account's provider). The subscription lives here, not in
827
+ * `useBaseQuery`.
828
+ *
829
+ * @example
830
+ * ```ts
831
+ * useBaseQuery({
832
+ * middlewares: [invalidateOnEvent({ event: 'walletAccountsChanged' })],
833
+ * queryFn: (client) => getConnectedAddresses(args, client),
834
+ * queryKey: ['useGetConnectedAddresses', args],
835
+ * });
836
+ * ```
837
+ */
838
+ const invalidateOnEvent = ({ event }) => (context) => {
839
+ const queryClient = useQueryClient();
840
+ const client = useDynamicClient();
841
+ useOnEvent({
842
+ event,
843
+ listener: (() => {
844
+ if (!context) return;
845
+ getCore(client).logger.debug("[invalidateOnEvent] invalidating query on event", {
846
+ event,
847
+ queryKey: context.queryKey
848
+ });
849
+ queryClient.invalidateQueries({ queryKey: context.queryKey }).catch((error) => {
850
+ getCore(client).logger.debug("[invalidateOnEvent] failed to invalidate query", {
851
+ error,
852
+ queryKey: context.queryKey,
853
+ stack: error instanceof Error ? error.stack : void 0
854
+ });
855
+ });
856
+ })
857
+ });
858
+ };
859
+
654
860
  //#endregion
655
861
  //#region src/hooks/wrappers/functions/useGetConnectedAddresses/useGetConnectedAddresses.ts
656
862
  /**
657
863
  * Returns the connected addresses for the given wallet provider.
658
864
  *
659
- * Wraps `getConnectedAddresses` from `@dynamic-labs-sdk/client`.
865
+ * Wraps `getConnectedAddresses` from `@dynamic-labs-sdk/client`. Automatically
866
+ * refetches when wallet accounts change (connect / disconnect / verify), so
867
+ * consumers don't have to pair it with a manual `walletAccountsChanged`
868
+ * listener.
660
869
  */
661
870
  const useGetConnectedAddresses = ({ queryParams, ...args }) => useBaseQuery({
871
+ middlewares: [invalidateOnEvent({ event: "walletAccountsChanged" })],
662
872
  queryFn: (client) => getConnectedAddresses(args, client),
663
873
  queryKey: ["useGetConnectedAddresses", args],
664
874
  queryParams
@@ -764,10 +974,13 @@ const useCreateNewMfaRecoveryCodes = ({ mutateParams } = {}) => useBaseMutation(
764
974
  * </button>
765
975
  * ```
766
976
  */
767
- const useDeleteMfaDevice = ({ mutateParams } = {}) => useBaseMutation({
768
- mutateParams,
769
- mutationFn: ({ client, variables }) => deleteMfaDevice(variables, client)
770
- });
977
+ const useDeleteMfaDevice = ({ mutateParams } = {}) => {
978
+ return useBaseMutation({
979
+ middlewares: [invalidatesQueries("useGetMfaDevices")],
980
+ mutateParams,
981
+ mutationFn: ({ client, variables }) => deleteMfaDevice(variables, client)
982
+ });
983
+ };
771
984
 
772
985
  //#endregion
773
986
  //#region src/hooks/wrappers/functions/useDeletePasskey/useDeletePasskey.ts
@@ -787,6 +1000,7 @@ const useDeleteMfaDevice = ({ mutateParams } = {}) => useBaseMutation({
787
1000
  * ```
788
1001
  */
789
1002
  const useDeletePasskey = ({ mutateParams } = {}) => useBaseMutation({
1003
+ middlewares: [invalidatesQueries("useGetPasskeys")],
790
1004
  mutateParams,
791
1005
  mutationFn: ({ client, variables }) => deletePasskey(variables, client)
792
1006
  });
@@ -813,26 +1027,6 @@ const useDeleteUser = ({ mutateParams } = {}) => useBaseMutation({
813
1027
  mutationFn: ({ client }) => deleteUser(client)
814
1028
  });
815
1029
 
816
- //#endregion
817
- //#region src/hooks/wrappers/functions/useOnEvent/useOnEvent.ts
818
- /**
819
- * Subscribes to a Dynamic client event.
820
- *
821
- * Uses a ref to always call the latest listener without re-subscribing,
822
- * and cleans up the subscription on unmount or event name change.
823
- */
824
- const useOnEvent = ({ event, listener }) => {
825
- const client = useDynamicClient();
826
- const callbackRef = useRef(listener);
827
- callbackRef.current = listener;
828
- useEffect(() => onEvent({
829
- event,
830
- listener: (...args) => {
831
- callbackRef.current(...args);
832
- }
833
- }, client), [event, client]);
834
- };
835
-
836
1030
  //#endregion
837
1031
  //#region src/hooks/wrappers/functions/useExecuteSwapTransaction/useExecuteSwapTransaction.ts
838
1032
  /**
@@ -911,17 +1105,73 @@ const usePrepareFlowSigning = ({ mutateParams } = {}) => useBaseMutation({
911
1105
  mutationFn: ({ client, variables }) => prepareFlowSigning(variables, client)
912
1106
  });
913
1107
 
1108
+ //#endregion
1109
+ //#region src/middlewares/invalidateOnUserSwitch/invalidateOnUserSwitch.ts
1110
+ const USER_CHANGED_EVENT = "userChanged";
1111
+ /**
1112
+ * Query middleware factory that refetches the query when a *different* user
1113
+ * becomes the current user (login or account switch).
1114
+ *
1115
+ * Unlike `invalidateOnEvent({ event: 'userChanged' })`, this never invalidates
1116
+ * on:
1117
+ * - logout (the user becomes `undefined`) — the refetch would just 401 and the
1118
+ * cleared session leaves nothing to refetch; and
1119
+ * - same-user `userChanged` fires (e.g. linking a credential or a profile
1120
+ * update) — the data still belongs to the same user, whose own mutations
1121
+ * already invalidate it within the session.
1122
+ *
1123
+ * It tracks the previous user id across renders (seeded from the user present
1124
+ * at mount) and only invalidates when the new id is defined and differs.
1125
+ *
1126
+ * @example
1127
+ * ```ts
1128
+ * useBaseQuery({
1129
+ * middlewares: [invalidateOnUserSwitch()],
1130
+ * queryFn: (client) => getMfaDevices(client),
1131
+ * queryKey: ['useGetMfaDevices'],
1132
+ * });
1133
+ * ```
1134
+ */
1135
+ const invalidateOnUserSwitch = () => (context) => {
1136
+ const client = useDynamicClient();
1137
+ const queryClient = useQueryClient();
1138
+ const previousUserIdRef = useRef(client.user?.id);
1139
+ useOnEvent({
1140
+ event: USER_CHANGED_EVENT,
1141
+ listener: ((args) => {
1142
+ const nextUserId = args.user?.id;
1143
+ const previousUserId = previousUserIdRef.current;
1144
+ previousUserIdRef.current = nextUserId;
1145
+ if (nextUserId === void 0 || nextUserId === previousUserId) return;
1146
+ if (!context) return;
1147
+ getCore(client).logger.debug("[invalidateOnUserSwitch] invalidating query on new user", { queryKey: context.queryKey });
1148
+ queryClient.invalidateQueries({ queryKey: context.queryKey }).catch((error) => {
1149
+ getCore(client).logger.debug("[invalidateOnUserSwitch] failed to invalidate query", {
1150
+ error,
1151
+ queryKey: context.queryKey,
1152
+ stack: error instanceof Error ? error.stack : void 0
1153
+ });
1154
+ });
1155
+ })
1156
+ });
1157
+ };
1158
+
914
1159
  //#endregion
915
1160
  //#region src/hooks/wrappers/functions/useGetGoogleDriveBackupReadiness/useGetGoogleDriveBackupReadiness.ts
916
1161
  /**
917
1162
  * Wraps `getGoogleDriveBackupReadiness` from `@dynamic-labs-sdk/client`. Reports whether the user's Google Drive backup is set up and available.
918
1163
  *
1164
+ * Automatically refetches when a different user becomes current (login or
1165
+ * account switch), so consumers don't have to pair it with a manual
1166
+ * `userChanged` listener.
1167
+ *
919
1168
  * @example
920
1169
  * ```tsx
921
1170
  * const { data: readiness, isLoading } = useGetGoogleDriveBackupReadiness();
922
1171
  * ```
923
1172
  */
924
1173
  const useGetGoogleDriveBackupReadiness = ({ queryParams } = {}) => useBaseQuery({
1174
+ middlewares: [invalidateOnUserSwitch()],
925
1175
  queryFn: (client) => getGoogleDriveBackupReadiness(client),
926
1176
  queryKey: ["useGetGoogleDriveBackupReadiness"],
927
1177
  queryParams
@@ -945,12 +1195,17 @@ const useIsMfaRequiredForAction = ({ queryParams, ...args }) => useBaseQuery({
945
1195
  /**
946
1196
  * Wraps `getKrakenAccounts` from `@dynamic-labs-sdk/client`. Returns the authenticated user's Kraken accounts (with balances), optionally filtered by chain/network.
947
1197
  *
1198
+ * Automatically refetches when a different user becomes current (login or
1199
+ * account switch), so consumers don't have to pair it with a manual
1200
+ * `userChanged` listener.
1201
+ *
948
1202
  * @example
949
1203
  * ```tsx
950
1204
  * const { data: accounts, isLoading } = useGetKrakenAccounts();
951
1205
  * ```
952
1206
  */
953
1207
  const useGetKrakenAccounts = ({ queryParams, ...args } = {}) => useBaseQuery({
1208
+ middlewares: [invalidateOnUserSwitch()],
954
1209
  queryFn: (client) => getKrakenAccounts(args, client),
955
1210
  queryKey: ["useGetKrakenAccounts", args],
956
1211
  queryParams
@@ -977,12 +1232,17 @@ const useCreateKrakenExchangeTransfer = ({ mutateParams } = {}) => useBaseMutati
977
1232
  /**
978
1233
  * Wraps `getKrakenWhitelistedAddresses` from `@dynamic-labs-sdk/client`. Returns the destination addresses whitelisted for Kraken withdrawals.
979
1234
  *
1235
+ * Automatically refetches when a different user becomes current (login or
1236
+ * account switch), so consumers don't have to pair it with a manual
1237
+ * `userChanged` listener.
1238
+ *
980
1239
  * @example
981
1240
  * ```tsx
982
1241
  * const { data: addresses, isLoading } = useGetKrakenWhitelistedAddresses();
983
1242
  * ```
984
1243
  */
985
1244
  const useGetKrakenWhitelistedAddresses = ({ queryParams } = {}) => useBaseQuery({
1245
+ middlewares: [invalidateOnUserSwitch()],
986
1246
  queryFn: (client) => getKrakenWhitelistedAddresses(client),
987
1247
  queryKey: ["useGetKrakenWhitelistedAddresses"],
988
1248
  queryParams
@@ -1015,12 +1275,17 @@ const useLogout = ({ mutateParams } = {}) => useBaseMutation({
1015
1275
  *
1016
1276
  * Wraps `getMfaDevices` from `@dynamic-labs-sdk/client`.
1017
1277
  *
1278
+ * Automatically refetches when a different user becomes current (login or
1279
+ * account switch), so consumers don't have to pair it with a manual
1280
+ * `userChanged` listener.
1281
+ *
1018
1282
  * @example
1019
1283
  * ```tsx
1020
1284
  * const { data: mfaDevices, isLoading, refetch } = useGetMfaDevices();
1021
1285
  * ```
1022
1286
  */
1023
1287
  const useGetMfaDevices = ({ queryParams } = {}) => useBaseQuery({
1288
+ middlewares: [invalidateOnUserSwitch()],
1024
1289
  queryFn: (client) => getMfaDevices(client),
1025
1290
  queryKey: ["useGetMfaDevices"],
1026
1291
  queryParams
@@ -1034,12 +1299,17 @@ const useGetMfaDevices = ({ queryParams } = {}) => useBaseQuery({
1034
1299
  *
1035
1300
  * Wraps `getMfaMethods` from `@dynamic-labs-sdk/client`.
1036
1301
  *
1302
+ * Automatically refetches when a different user becomes current (login or
1303
+ * account switch), so consumers don't have to pair it with a manual
1304
+ * `userChanged` listener.
1305
+ *
1037
1306
  * @example
1038
1307
  * ```tsx
1039
1308
  * const { data: mfaMethods, isLoading, refetch } = useGetMfaMethods();
1040
1309
  * ```
1041
1310
  */
1042
1311
  const useGetMfaMethods = ({ queryParams } = {}) => useBaseQuery({
1312
+ middlewares: [invalidateOnUserSwitch()],
1043
1313
  queryFn: (client) => getMfaMethods(client),
1044
1314
  queryKey: ["useGetMfaMethods"],
1045
1315
  queryParams
@@ -1052,12 +1322,17 @@ const useGetMfaMethods = ({ queryParams } = {}) => useBaseQuery({
1052
1322
  *
1053
1323
  * Wraps `getMfaRecoveryCodes` from `@dynamic-labs-sdk/client`.
1054
1324
  *
1325
+ * Automatically refetches when a different user becomes current (login or
1326
+ * account switch), so consumers don't have to pair it with a manual
1327
+ * `userChanged` listener.
1328
+ *
1055
1329
  * @example
1056
1330
  * ```tsx
1057
1331
  * const { data: recoveryCodes, isLoading, refetch } = useGetMfaRecoveryCodes();
1058
1332
  * ```
1059
1333
  */
1060
1334
  const useGetMfaRecoveryCodes = ({ queryParams } = {}) => useBaseQuery({
1335
+ middlewares: [invalidateOnUserSwitch()],
1061
1336
  queryFn: (client) => getMfaRecoveryCodes(client),
1062
1337
  queryKey: ["useGetMfaRecoveryCodes"],
1063
1338
  queryParams
@@ -1135,6 +1410,10 @@ const useGetMultichainTokenBalances = ({ balanceRequest, queryParams }) => useBa
1135
1410
  */
1136
1411
  const useGetNativeBalance = ({ walletAccount, queryParams }) => useBaseQuery({
1137
1412
  enabled: walletAccount !== void 0,
1413
+ middlewares: [invalidateOnWalletProviderEvent({
1414
+ event: "networkChanged",
1415
+ walletAccount
1416
+ })],
1138
1417
  queryFn: (client) => getNativeBalance({ walletAccount }, client),
1139
1418
  queryKey: ["useGetNativeBalance", walletAccount?.id],
1140
1419
  queryParams
@@ -1176,12 +1455,17 @@ const useOnceEvent = ({ event, listener }) => {
1176
1455
  *
1177
1456
  * Wraps `getPasskeys` from `@dynamic-labs-sdk/client`.
1178
1457
  *
1458
+ * Automatically refetches when a different user becomes current (login or
1459
+ * account switch), so consumers don't have to pair it with a manual
1460
+ * `userChanged` listener.
1461
+ *
1179
1462
  * @example
1180
1463
  * ```tsx
1181
1464
  * const { data: passkeys, isLoading, refetch } = useGetPasskeys();
1182
1465
  * ```
1183
1466
  */
1184
1467
  const useGetPasskeys = ({ queryParams } = {}) => useBaseQuery({
1468
+ middlewares: [invalidateOnUserSwitch()],
1185
1469
  queryFn: (client) => getPasskeys(client),
1186
1470
  queryKey: ["useGetPasskeys"],
1187
1471
  queryParams
@@ -1231,12 +1515,17 @@ const useRefreshAuth = ({ mutateParams } = {}) => useBaseMutation({
1231
1515
  * {@link useRevokeRegisteredDevice} to let users revoke a device from the
1232
1516
  * list.
1233
1517
  *
1518
+ * Automatically refetches when a different user becomes current (login or
1519
+ * account switch), so consumers don't have to pair it with a manual
1520
+ * `userChanged` listener.
1521
+ *
1234
1522
  * @example
1235
1523
  * ```tsx
1236
1524
  * const { data: devices, isLoading, refetch } = useGetRegisteredDevices();
1237
1525
  * ```
1238
1526
  */
1239
1527
  const useGetRegisteredDevices = ({ queryParams } = {}) => useBaseQuery({
1528
+ middlewares: [invalidateOnUserSwitch()],
1240
1529
  queryFn: (client) => getRegisteredDevices(client),
1241
1530
  queryKey: ["useGetRegisteredDevices"],
1242
1531
  queryParams
@@ -1260,6 +1549,7 @@ const useGetRegisteredDevices = ({ queryParams } = {}) => useBaseQuery({
1260
1549
  * ```
1261
1550
  */
1262
1551
  const useRegisterPasskey = ({ mutateParams } = {}) => useBaseMutation({
1552
+ middlewares: [invalidatesQueries("useGetPasskeys")],
1263
1553
  mutateParams,
1264
1554
  mutationFn: ({ client, variables }) => registerPasskey(variables, client)
1265
1555
  });
@@ -1282,6 +1572,7 @@ const useRegisterPasskey = ({ mutateParams } = {}) => useBaseMutation({
1282
1572
  * ```
1283
1573
  */
1284
1574
  const useRegisterTotpMfaDevice = ({ mutateParams } = {}) => useBaseMutation({
1575
+ middlewares: [invalidatesQueries("useGetMfaDevices")],
1285
1576
  mutateParams,
1286
1577
  mutationFn: ({ client }) => registerTotpMfaDevice(client)
1287
1578
  });
@@ -1342,10 +1633,13 @@ const useRequestExternalAuthElevatedToken = ({ mutateParams } = {}) => useBaseMu
1342
1633
  * </button>
1343
1634
  * ```
1344
1635
  */
1345
- const useRevokeAllRegisteredDevices = ({ mutateParams } = {}) => useBaseMutation({
1346
- mutateParams,
1347
- mutationFn: ({ client }) => revokeAllRegisteredDevices(client)
1348
- });
1636
+ const useRevokeAllRegisteredDevices = ({ mutateParams } = {}) => {
1637
+ return useBaseMutation({
1638
+ middlewares: [invalidatesQueries("useGetRegisteredDevices")],
1639
+ mutateParams,
1640
+ mutationFn: ({ client }) => revokeAllRegisteredDevices(client)
1641
+ });
1642
+ };
1349
1643
 
1350
1644
  //#endregion
1351
1645
  //#region src/hooks/wrappers/functions/useRevokeRegisteredDevice/useRevokeRegisteredDevice.ts
@@ -1368,10 +1662,13 @@ const useRevokeAllRegisteredDevices = ({ mutateParams } = {}) => useBaseMutation
1368
1662
  * </button>
1369
1663
  * ```
1370
1664
  */
1371
- const useRevokeRegisteredDevice = ({ mutateParams } = {}) => useBaseMutation({
1372
- mutateParams,
1373
- mutationFn: ({ client, variables }) => revokeRegisteredDevice(variables, client)
1374
- });
1665
+ const useRevokeRegisteredDevice = ({ mutateParams } = {}) => {
1666
+ return useBaseMutation({
1667
+ middlewares: [invalidatesQueries("useGetRegisteredDevices")],
1668
+ mutateParams,
1669
+ mutationFn: ({ client, variables }) => revokeRegisteredDevice(variables, client)
1670
+ });
1671
+ };
1375
1672
 
1376
1673
  //#endregion
1377
1674
  //#region src/hooks/wrappers/functions/useSendEmailOTP/useSendEmailOTP.ts
@@ -1446,6 +1743,7 @@ const useSendSmsOTP = ({ mutateParams } = {}) => useBaseMutation({
1446
1743
  * ```
1447
1744
  */
1448
1745
  const useSetDefaultMfaDevice = ({ mutateParams } = {}) => useBaseMutation({
1746
+ middlewares: [invalidatesQueries("useGetMfaDevices")],
1449
1747
  mutateParams,
1450
1748
  mutationFn: ({ client, variables }) => setDefaultMfaDevice(variables, client)
1451
1749
  });
@@ -1622,6 +1920,7 @@ const useGetSwapStatus = ({ queryParams, ...args }) => useBaseQuery({
1622
1920
  * ```
1623
1921
  */
1624
1922
  const useSwitchActiveNetwork = ({ mutateParams } = {}) => useBaseMutation({
1923
+ middlewares: [invalidatesQueries("useGetActiveNetworkData", "useGetActiveNetworkId")],
1625
1924
  mutateParams,
1626
1925
  mutationFn: ({ client, variables }) => switchActiveNetwork(variables, client)
1627
1926
  });
@@ -1645,6 +1944,10 @@ const useSwitchActiveNetwork = ({ mutateParams } = {}) => useBaseMutation({
1645
1944
  */
1646
1945
  const useGetTokenBalances = ({ walletAccount, queryParams, ...rest }) => useBaseQuery({
1647
1946
  enabled: walletAccount !== void 0,
1947
+ middlewares: [invalidateOnWalletProviderEvent({
1948
+ event: "networkChanged",
1949
+ walletAccount
1950
+ })],
1648
1951
  queryFn: (client) => getTokenBalances({
1649
1952
  walletAccount,
1650
1953
  ...rest
@@ -1907,54 +2210,43 @@ const useGetWalletConnectCatalogWalletByWalletProviderKey = ({ queryParams, ...a
1907
2210
  //#endregion
1908
2211
  //#region src/hooks/wrappers/functions/useGetWalletOptionsCatalogue/useGetWalletOptionsCatalogue.ts
1909
2212
  /**
2213
+ * Freshness window for the catalogue, in milliseconds.
2214
+ *
2215
+ * The catalogue is derived from two near-static sources: the in-memory wallet
2216
+ * provider registry and the wallet-book document served from a CDN. With
2217
+ * react-query's default `staleTime: 0` the data is stale the instant it
2218
+ * arrives, so every new mount/observer (e.g. reopening the wallet list)
2219
+ * refetches the CDN document even though nothing changed — needlessly
2220
+ * hammering the CDN. A 1h window serves cached data to remounts; the registry
2221
+ * event middlewares below still force an immediate refetch on any real change,
2222
+ * so reactivity is preserved.
2223
+ */
2224
+ const CATALOGUE_STALE_TIME_MS = 36e5;
2225
+ /**
1910
2226
  * Wraps `getWalletOptionsCatalogue` from `@dynamic-labs-sdk/client`. Returns the catalogue of wallet options the project supports.
1911
2227
  *
2228
+ * Automatically refetches when the wallet provider registry changes: EIP-6963
2229
+ * wallets fill the registry asynchronously after mount, and the registry emits
2230
+ * `walletProviderChanged` (on register / priority update) and
2231
+ * `walletProviderUnregistered` (on removal). Wiring both keeps the catalogue in
2232
+ * sync without consumers pairing the hook with a manual listener.
2233
+ *
1912
2234
  * @example
1913
2235
  * ```tsx
1914
2236
  * const { data: catalogue, isLoading } = useGetWalletOptionsCatalogue();
1915
2237
  * ```
1916
2238
  */
1917
2239
  const useGetWalletOptionsCatalogue = ({ queryParams, ...args } = {}) => useBaseQuery({
2240
+ middlewares: [invalidateOnEvent({ event: "walletProviderChanged" }), invalidateOnEvent({ event: "walletProviderUnregistered" })],
1918
2241
  queryFn: (client) => getWalletOptionsCatalogue(args, client),
1919
2242
  queryKey: ["useGetWalletOptionsCatalogue", args],
1920
- queryParams
2243
+ queryParams: {
2244
+ retry: false,
2245
+ staleTime: CATALOGUE_STALE_TIME_MS,
2246
+ ...queryParams
2247
+ }
1921
2248
  });
1922
2249
 
1923
- //#endregion
1924
- //#region src/hooks/wrappers/functions/useOnWalletProviderEvent/useOnWalletProviderEvent.ts
1925
- /**
1926
- * Subscribes to an event emitted by a specific wallet provider.
1927
- *
1928
- * Wraps `onWalletProviderEvent` from `@dynamic-labs-sdk/client`. Uses a ref to
1929
- * always call the latest callback without re-subscribing, and cleans up the
1930
- * subscription on unmount or when `event` / `walletProviderKey` change.
1931
- *
1932
- * @example
1933
- * ```tsx
1934
- * useOnWalletProviderEvent({
1935
- * walletProviderKey: 'metamaskevm',
1936
- * event: 'accountsChanged',
1937
- * callback: ({ addresses }) => console.log(addresses),
1938
- * });
1939
- * ```
1940
- */
1941
- const useOnWalletProviderEvent = ({ callback, event, walletProviderKey }) => {
1942
- const client = useDynamicClient();
1943
- const callbackRef = useRef(callback);
1944
- callbackRef.current = callback;
1945
- useEffect(() => onWalletProviderEvent({
1946
- callback: ((...args) => {
1947
- callbackRef.current(...args);
1948
- }),
1949
- event,
1950
- walletProviderKey
1951
- }, client), [
1952
- event,
1953
- walletProviderKey,
1954
- client
1955
- ]);
1956
- };
1957
-
1958
2250
  //#endregion
1959
2251
  //#region src/hooks/wrappers/state/useInitStatus/useInitStatus.ts
1960
2252
  /**