@dynamic-labs/sdk-react-core 4.90.0 → 4.91.0

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 (34) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/README.md +1 -52
  3. package/package.cjs +2 -2
  4. package/package.js +2 -2
  5. package/package.json +13 -13
  6. package/src/lib/client/extension/functions/getSessionKeys/getSessionKeys.cjs +11 -0
  7. package/src/lib/client/extension/functions/getSessionKeys/getSessionKeys.js +7 -0
  8. package/src/lib/client/extension/functions/getSessionPublicKey/getSessionPublicKey.cjs +17 -0
  9. package/src/lib/client/extension/functions/getSessionPublicKey/getSessionPublicKey.d.ts +7 -0
  10. package/src/lib/client/extension/functions/getSessionPublicKey/getSessionPublicKey.js +13 -0
  11. package/src/lib/client/extension/index.d.ts +1 -0
  12. package/src/lib/context/DynamicContext/DynamicContext.cjs +9 -1
  13. package/src/lib/context/DynamicContext/DynamicContext.js +10 -2
  14. package/src/lib/styles/index.shadow.cjs +1 -1
  15. package/src/lib/styles/index.shadow.js +1 -1
  16. package/src/lib/utils/functions/getTransactionLink/blockExplorerPatterns.cjs +12 -0
  17. package/src/lib/utils/functions/getTransactionLink/blockExplorerPatterns.js +12 -0
  18. package/src/lib/utils/hooks/useCreateWalletConnectorOptions/utils/verifyZeroDevIsSetup/verifyZeroDevIsSetup.cjs +7 -3
  19. package/src/lib/utils/hooks/useCreateWalletConnectorOptions/utils/verifyZeroDevIsSetup/verifyZeroDevIsSetup.js +7 -3
  20. package/src/lib/utils/hooks/useDynamicWaas/useDynamicWaas.cjs +16 -3
  21. package/src/lib/utils/hooks/useDynamicWaas/useDynamicWaas.d.ts +3 -1
  22. package/src/lib/utils/hooks/useDynamicWaas/useDynamicWaas.js +16 -3
  23. package/src/lib/utils/hooks/useSetWalletConnectorFetchers/useSetWalletConnectorFetchers.cjs +8 -1
  24. package/src/lib/utils/hooks/useSetWalletConnectorFetchers/useSetWalletConnectorFetchers.d.ts +1 -1
  25. package/src/lib/utils/hooks/useSetWalletConnectorFetchers/useSetWalletConnectorFetchers.js +8 -1
  26. package/src/lib/views/TransactionConfirmationView/helpers/transactionErrorMessage.cjs +8 -0
  27. package/src/lib/views/TransactionConfirmationView/helpers/transactionErrorMessage.js +8 -0
  28. package/src/lib/widgets/DynamicWidget/components/ActiveMidnightWalletBalance/ActiveMidnightWalletBalance.cjs +56 -13
  29. package/src/lib/widgets/DynamicWidget/components/ActiveMidnightWalletBalance/ActiveMidnightWalletBalance.js +58 -15
  30. package/src/lib/widgets/DynamicWidget/components/ActiveWalletInformation/ActiveMidnightWalletAddresses/ActiveMidnightWalletAddresses.cjs +40 -2
  31. package/src/lib/widgets/DynamicWidget/components/ActiveWalletInformation/ActiveMidnightWalletAddresses/ActiveMidnightWalletAddresses.d.ts +7 -0
  32. package/src/lib/widgets/DynamicWidget/components/ActiveWalletInformation/ActiveMidnightWalletAddresses/ActiveMidnightWalletAddresses.js +41 -3
  33. package/src/lib/widgets/DynamicWidget/components/ActiveWalletInformation/ActiveWalletInformation.cjs +2 -0
  34. package/src/lib/widgets/DynamicWidget/components/ActiveWalletInformation/ActiveWalletInformation.js +2 -0
@@ -32,6 +32,18 @@ const BLOCK_EXPLORER_PATTERNS = [
32
32
  return url.toString();
33
33
  },
34
34
  },
35
+ {
36
+ // The Midnight explorer indexes by the canonical transaction hash and
37
+ // serves detail pages at `/transactions/{hash}`. Matching the bare
38
+ // `midnight.network` substring covers the preview/testnet subdomains.
39
+ description: 'Midnight - Uses /transactions/{hash}',
40
+ domain: 'midnight.network',
41
+ generateUrl: (baseUrl, hash) => {
42
+ const url = new URL(baseUrl);
43
+ url.pathname = `/transactions/${hash}`;
44
+ return url.toString();
45
+ },
46
+ },
35
47
  ];
36
48
  /**
37
49
  * Default pattern for standard block explorers
@@ -28,6 +28,18 @@ const BLOCK_EXPLORER_PATTERNS = [
28
28
  return url.toString();
29
29
  },
30
30
  },
31
+ {
32
+ // The Midnight explorer indexes by the canonical transaction hash and
33
+ // serves detail pages at `/transactions/{hash}`. Matching the bare
34
+ // `midnight.network` substring covers the preview/testnet subdomains.
35
+ description: 'Midnight - Uses /transactions/{hash}',
36
+ domain: 'midnight.network',
37
+ generateUrl: (baseUrl, hash) => {
38
+ const url = new URL(baseUrl);
39
+ url.pathname = `/transactions/${hash}`;
40
+ return url.toString();
41
+ },
42
+ },
31
43
  ];
32
44
  /**
33
45
  * Default pattern for standard block explorers
@@ -8,7 +8,7 @@ require('../../../../constants/values.cjs');
8
8
  require('@dynamic-labs/utils');
9
9
  require('../../../../../../../_virtual/_tslib.cjs');
10
10
  require('@dynamic-labs/multi-wallet');
11
- require('../../../../../shared/logger.cjs');
11
+ var logger = require('../../../../../shared/logger.cjs');
12
12
  require('react-international-phone');
13
13
  require('../../../../constants/colors.cjs');
14
14
  require('@dynamic-labs/iconic');
@@ -31,7 +31,6 @@ require('../../../../../store/state/dynamicContextProps/dynamicContextProps.cjs'
31
31
  require('../../../../../store/state/primaryWalletId/primaryWalletId.cjs');
32
32
  require('../../../../../store/state/connectedWalletsInfo/connectedWalletsInfo.cjs');
33
33
  require('../../../../functions/getWaasAddressTypeLabel/getWaasAddressTypeLabel.cjs');
34
- var ConnectorSetupError = require('../errors/ConnectorSetupError.cjs');
35
34
 
36
35
  const zeroDevWalletConnectorKey = 'zerodev';
37
36
  const verifyZeroDevIsSetup = (projectSettings, walletConnectors) => {
@@ -39,7 +38,12 @@ const verifyZeroDevIsSetup = (projectSettings, walletConnectors) => {
39
38
  const isZeroDevEnabled = isProviderEnabled.isProviderEnabled((_a = projectSettings.providers) !== null && _a !== void 0 ? _a : [], sdkApiCore.ProviderEnum.Zerodev);
40
39
  const isZeroDevWalletConnectorPresent = walletConnectors.some(({ key }) => key === zeroDevWalletConnectorKey);
41
40
  if (isZeroDevEnabled && !isZeroDevWalletConnectorPresent) {
42
- throw new ConnectorSetupError.ConnectorSetupError('ZeroDev', 'ZeroDevSmartWalletConnectors');
41
+ // A ZeroDev connector that's enabled in project settings but absent from
42
+ // `walletConnectors` is an explicit decision to migrate off ZeroDev —
43
+ // whether to native EVM gas sponsorship or to plain EOAs with no
44
+ // sponsorship at all. Warn and continue (wallets fall back to the embedded
45
+ // EOA) instead of hard-throwing and white-screening the app.
46
+ logger.logger.warn('ZeroDev is enabled in project settings but ZeroDevSmartWalletConnectors is not in walletConnectors. Continuing without ZeroDev — wallets will fall back to the embedded EOA (gas is sponsored only if native EVM gas sponsorship is enabled).');
43
47
  }
44
48
  };
45
49
 
@@ -4,7 +4,7 @@ import '../../../../constants/values.js';
4
4
  import '@dynamic-labs/utils';
5
5
  import '../../../../../../../_virtual/_tslib.js';
6
6
  import '@dynamic-labs/multi-wallet';
7
- import '../../../../../shared/logger.js';
7
+ import { logger } from '../../../../../shared/logger.js';
8
8
  import 'react-international-phone';
9
9
  import '../../../../constants/colors.js';
10
10
  import '@dynamic-labs/iconic';
@@ -27,7 +27,6 @@ import '../../../../../store/state/dynamicContextProps/dynamicContextProps.js';
27
27
  import '../../../../../store/state/primaryWalletId/primaryWalletId.js';
28
28
  import '../../../../../store/state/connectedWalletsInfo/connectedWalletsInfo.js';
29
29
  import '../../../../functions/getWaasAddressTypeLabel/getWaasAddressTypeLabel.js';
30
- import { ConnectorSetupError } from '../errors/ConnectorSetupError.js';
31
30
 
32
31
  const zeroDevWalletConnectorKey = 'zerodev';
33
32
  const verifyZeroDevIsSetup = (projectSettings, walletConnectors) => {
@@ -35,7 +34,12 @@ const verifyZeroDevIsSetup = (projectSettings, walletConnectors) => {
35
34
  const isZeroDevEnabled = isProviderEnabled((_a = projectSettings.providers) !== null && _a !== void 0 ? _a : [], ProviderEnum.Zerodev);
36
35
  const isZeroDevWalletConnectorPresent = walletConnectors.some(({ key }) => key === zeroDevWalletConnectorKey);
37
36
  if (isZeroDevEnabled && !isZeroDevWalletConnectorPresent) {
38
- throw new ConnectorSetupError('ZeroDev', 'ZeroDevSmartWalletConnectors');
37
+ // A ZeroDev connector that's enabled in project settings but absent from
38
+ // `walletConnectors` is an explicit decision to migrate off ZeroDev —
39
+ // whether to native EVM gas sponsorship or to plain EOAs with no
40
+ // sponsorship at all. Warn and continue (wallets fall back to the embedded
41
+ // EOA) instead of hard-throwing and white-screening the app.
42
+ logger.warn('ZeroDev is enabled in project settings but ZeroDevSmartWalletConnectors is not in walletConnectors. Continuing without ZeroDev — wallets will fall back to the embedded EOA (gas is sponsored only if native EVM gas sponsorship is enabled).');
39
43
  }
40
44
  };
41
45
 
@@ -39,6 +39,7 @@ var useProjectSettings = require('../../../client/extension/projectSettings/useP
39
39
  var isCookieEnabled = require('../../../client/extension/functions/isCookieEnabled/isCookieEnabled.cjs');
40
40
  var getMinAuthToken = require('../../../client/extension/functions/getMinAuthToken/getMinAuthToken.cjs');
41
41
  var generateChainingSignature = require('../../../client/extension/functions/generateChainingSignature/generateChainingSignature.cjs');
42
+ var getSessionPublicKey = require('../../../client/extension/functions/getSessionPublicKey/getSessionPublicKey.cjs');
42
43
  require('../../../context/DynamicContext/useDynamicContext/useDynamicContext.cjs');
43
44
  var useInternalDynamicContext = require('../../../context/DynamicContext/useDynamicContext/useInternalDynamicContext/useInternalDynamicContext.cjs');
44
45
  var UserWalletsContext = require('../../../context/UserWalletsContext/UserWalletsContext.cjs');
@@ -55,8 +56,8 @@ var normalizeWaasChainName = require('./normalizeWaasChainName.cjs');
55
56
  var normalizeWalletRequirements = require('./normalizeWalletRequirements.cjs');
56
57
 
57
58
  const dynamicWaasKey = 'dynamicwaas';
58
- const configWaasWalletConnector = ({ walletConnector, environmentId, apiBaseUrl, relayUrl, baseClientKeysharesRelayApiUrl, authMode = 'header', getSignedSessionId, getMfaToken, getWalletPassword, getSvmGasSponsorshipEnabled = () => false, getElevatedAccessToken, }) => {
59
- var _a;
59
+ const configWaasWalletConnector = ({ walletConnector, environmentId, apiBaseUrl, relayUrl, baseClientKeysharesRelayApiUrl, authMode = 'header', getSignedSessionId, getSessionPublicKey, getMfaToken, getWalletPassword, getSvmGasSponsorshipEnabled = () => false, getElevatedAccessToken, onUnauthorized, }) => {
60
+ var _a, _b;
60
61
  walletConnector.setEnvironmentId(environmentId);
61
62
  walletConnector.setBaseApiUrl((_a = (apiBaseUrl || apiUrl.DEFAULT_WAAS_API_URL)) === null || _a === void 0 ? void 0 : _a.replace('/api/v0', ''));
62
63
  if (authMode === 'cookie') {
@@ -68,6 +69,11 @@ const configWaasWalletConnector = ({ walletConnector, environmentId, apiBaseUrl,
68
69
  walletConnector.setGetAuthTokenFunction(getMinAuthToken.getMinAuthToken);
69
70
  }
70
71
  walletConnector.setGetSignedSessionIdFunction(getSignedSessionId);
72
+ // Optional + best-effort: a connector build predating this method must not
73
+ // break wiring — the backup pubkey header simply stays absent.
74
+ if (getSessionPublicKey) {
75
+ (_b = walletConnector.setGetSessionPublicKeyFunction) === null || _b === void 0 ? void 0 : _b.call(walletConnector, getSessionPublicKey);
76
+ }
71
77
  walletConnector.setRelayUrl(relayUrl);
72
78
  walletConnector.setGetMfaTokenFunction(getMfaToken);
73
79
  walletConnector.setBaseClientKeysharesRelayApiUrl(baseClientKeysharesRelayApiUrl);
@@ -79,6 +85,10 @@ const configWaasWalletConnector = ({ walletConnector, environmentId, apiBaseUrl,
79
85
  if (getWalletPassword) {
80
86
  walletConnector.setGetWalletPasswordFunction(getWalletPassword);
81
87
  }
88
+ // Log the user out when the iframe reports a 401 (e.g. token forcibly revoked)
89
+ if (onUnauthorized) {
90
+ walletConnector.setOnUnauthorizedFunction(onUnauthorized);
91
+ }
82
92
  // Sync SVM gas sponsorship setting for SOL connectors
83
93
  const isSolConnector = walletConnector.connectedChain === 'SOL';
84
94
  const supportsSponsorshipSetting = 'setSvmGasSponsorshipEnabled' in walletConnector;
@@ -89,7 +99,7 @@ const configWaasWalletConnector = ({ walletConnector, environmentId, apiBaseUrl,
89
99
  };
90
100
  const useDynamicWaas = () => {
91
101
  var _a, _b, _c, _d, _e, _f;
92
- const { setShowAuthFlow } = useInternalDynamicContext.useInternalDynamicContext();
102
+ const { setShowAuthFlow, handleLogOutWithReason } = useInternalDynamicContext.useInternalDynamicContext();
93
103
  const { addedWalletsIds, userWallets } = UserWalletsContext.useInternalUserWallets();
94
104
  const user = useUser.useUser();
95
105
  const apiBaseUrl = dynamicContextProps.useApiBaseUrl();
@@ -149,9 +159,11 @@ const useDynamicWaas = () => {
149
159
  environmentId,
150
160
  getElevatedAccessToken,
151
161
  getMfaToken,
162
+ getSessionPublicKey: getSessionPublicKey.getSessionPublicKey,
152
163
  getSignedSessionId: generateChainingSignature.generateChainingSignature,
153
164
  getSvmGasSponsorshipEnabled: () => svmGasSponsorshipEnabledRef.current,
154
165
  getWalletPassword,
166
+ onUnauthorized: () => handleLogOutWithReason('session-refresh-unauthorized'),
155
167
  relayUrl: relayUrl !== null && relayUrl !== void 0 ? relayUrl : '',
156
168
  walletConnector,
157
169
  });
@@ -165,6 +177,7 @@ const useDynamicWaas = () => {
165
177
  getElevatedAccessToken,
166
178
  getMfaToken,
167
179
  getWalletPassword,
180
+ handleLogOutWithReason,
168
181
  relayUrl,
169
182
  baseClientKeysharesRelayApiUrl,
170
183
  ]);
@@ -12,7 +12,7 @@ export type WalletCreationRequirement = {
12
12
  network?: string;
13
13
  };
14
14
  };
15
- export declare const configWaasWalletConnector: ({ walletConnector, environmentId, apiBaseUrl, relayUrl, baseClientKeysharesRelayApiUrl, authMode, getSignedSessionId, getMfaToken, getWalletPassword, getSvmGasSponsorshipEnabled, getElevatedAccessToken, }: {
15
+ export declare const configWaasWalletConnector: ({ walletConnector, environmentId, apiBaseUrl, relayUrl, baseClientKeysharesRelayApiUrl, authMode, getSignedSessionId, getSessionPublicKey, getMfaToken, getWalletPassword, getSvmGasSponsorshipEnabled, getElevatedAccessToken, onUnauthorized, }: {
16
16
  walletConnector: IDynamicWaasConnector;
17
17
  environmentId: string;
18
18
  apiBaseUrl: string;
@@ -20,6 +20,7 @@ export declare const configWaasWalletConnector: ({ walletConnector, environmentI
20
20
  baseClientKeysharesRelayApiUrl?: string;
21
21
  authMode?: 'cookie' | 'header';
22
22
  getSignedSessionId: () => Promise<string>;
23
+ getSessionPublicKey?: () => Promise<string | undefined>;
23
24
  getMfaToken: (props?: {
24
25
  mfaAction?: MFAAction;
25
26
  }) => Promise<string | undefined>;
@@ -28,6 +29,7 @@ export declare const configWaasWalletConnector: ({ walletConnector, environmentI
28
29
  getElevatedAccessToken?: (params: {
29
30
  scope: TokenScope;
30
31
  }) => Promise<string | undefined>;
32
+ onUnauthorized?: () => void | Promise<void>;
31
33
  }) => IDynamicWaasConnector;
32
34
  export declare const useDynamicWaas: () => {
33
35
  areWalletsReadyForSettings: () => boolean;
@@ -35,6 +35,7 @@ import { useProjectSettings } from '../../../client/extension/projectSettings/us
35
35
  import { isCookieEnabled } from '../../../client/extension/functions/isCookieEnabled/isCookieEnabled.js';
36
36
  import { getMinAuthToken } from '../../../client/extension/functions/getMinAuthToken/getMinAuthToken.js';
37
37
  import { generateChainingSignature } from '../../../client/extension/functions/generateChainingSignature/generateChainingSignature.js';
38
+ import { getSessionPublicKey } from '../../../client/extension/functions/getSessionPublicKey/getSessionPublicKey.js';
38
39
  import '../../../context/DynamicContext/useDynamicContext/useDynamicContext.js';
39
40
  import { useInternalDynamicContext } from '../../../context/DynamicContext/useDynamicContext/useInternalDynamicContext/useInternalDynamicContext.js';
40
41
  import { useInternalUserWallets } from '../../../context/UserWalletsContext/UserWalletsContext.js';
@@ -51,8 +52,8 @@ import { normalizeWaasChainName } from './normalizeWaasChainName.js';
51
52
  import { normalizeWalletRequirements } from './normalizeWalletRequirements.js';
52
53
 
53
54
  const dynamicWaasKey = 'dynamicwaas';
54
- const configWaasWalletConnector = ({ walletConnector, environmentId, apiBaseUrl, relayUrl, baseClientKeysharesRelayApiUrl, authMode = 'header', getSignedSessionId, getMfaToken, getWalletPassword, getSvmGasSponsorshipEnabled = () => false, getElevatedAccessToken, }) => {
55
- var _a;
55
+ const configWaasWalletConnector = ({ walletConnector, environmentId, apiBaseUrl, relayUrl, baseClientKeysharesRelayApiUrl, authMode = 'header', getSignedSessionId, getSessionPublicKey, getMfaToken, getWalletPassword, getSvmGasSponsorshipEnabled = () => false, getElevatedAccessToken, onUnauthorized, }) => {
56
+ var _a, _b;
56
57
  walletConnector.setEnvironmentId(environmentId);
57
58
  walletConnector.setBaseApiUrl((_a = (apiBaseUrl || DEFAULT_WAAS_API_URL)) === null || _a === void 0 ? void 0 : _a.replace('/api/v0', ''));
58
59
  if (authMode === 'cookie') {
@@ -64,6 +65,11 @@ const configWaasWalletConnector = ({ walletConnector, environmentId, apiBaseUrl,
64
65
  walletConnector.setGetAuthTokenFunction(getMinAuthToken);
65
66
  }
66
67
  walletConnector.setGetSignedSessionIdFunction(getSignedSessionId);
68
+ // Optional + best-effort: a connector build predating this method must not
69
+ // break wiring — the backup pubkey header simply stays absent.
70
+ if (getSessionPublicKey) {
71
+ (_b = walletConnector.setGetSessionPublicKeyFunction) === null || _b === void 0 ? void 0 : _b.call(walletConnector, getSessionPublicKey);
72
+ }
67
73
  walletConnector.setRelayUrl(relayUrl);
68
74
  walletConnector.setGetMfaTokenFunction(getMfaToken);
69
75
  walletConnector.setBaseClientKeysharesRelayApiUrl(baseClientKeysharesRelayApiUrl);
@@ -75,6 +81,10 @@ const configWaasWalletConnector = ({ walletConnector, environmentId, apiBaseUrl,
75
81
  if (getWalletPassword) {
76
82
  walletConnector.setGetWalletPasswordFunction(getWalletPassword);
77
83
  }
84
+ // Log the user out when the iframe reports a 401 (e.g. token forcibly revoked)
85
+ if (onUnauthorized) {
86
+ walletConnector.setOnUnauthorizedFunction(onUnauthorized);
87
+ }
78
88
  // Sync SVM gas sponsorship setting for SOL connectors
79
89
  const isSolConnector = walletConnector.connectedChain === 'SOL';
80
90
  const supportsSponsorshipSetting = 'setSvmGasSponsorshipEnabled' in walletConnector;
@@ -85,7 +95,7 @@ const configWaasWalletConnector = ({ walletConnector, environmentId, apiBaseUrl,
85
95
  };
86
96
  const useDynamicWaas = () => {
87
97
  var _a, _b, _c, _d, _e, _f;
88
- const { setShowAuthFlow } = useInternalDynamicContext();
98
+ const { setShowAuthFlow, handleLogOutWithReason } = useInternalDynamicContext();
89
99
  const { addedWalletsIds, userWallets } = useInternalUserWallets();
90
100
  const user = useUser();
91
101
  const apiBaseUrl = useApiBaseUrl();
@@ -145,9 +155,11 @@ const useDynamicWaas = () => {
145
155
  environmentId,
146
156
  getElevatedAccessToken,
147
157
  getMfaToken,
158
+ getSessionPublicKey,
148
159
  getSignedSessionId: generateChainingSignature,
149
160
  getSvmGasSponsorshipEnabled: () => svmGasSponsorshipEnabledRef.current,
150
161
  getWalletPassword,
162
+ onUnauthorized: () => handleLogOutWithReason('session-refresh-unauthorized'),
151
163
  relayUrl: relayUrl !== null && relayUrl !== void 0 ? relayUrl : '',
152
164
  walletConnector,
153
165
  });
@@ -161,6 +173,7 @@ const useDynamicWaas = () => {
161
173
  getElevatedAccessToken,
162
174
  getMfaToken,
163
175
  getWalletPassword,
176
+ handleLogOutWithReason,
164
177
  relayUrl,
165
178
  baseClientKeysharesRelayApiUrl,
166
179
  ]);
@@ -31,6 +31,7 @@ require('@dynamic-labs-sdk/client');
31
31
  require('../../../config/ApiEndpoint.cjs');
32
32
  var isCookieEnabled = require('../../../client/extension/functions/isCookieEnabled/isCookieEnabled.cjs');
33
33
  var generateChainingSignature = require('../../../client/extension/functions/generateChainingSignature/generateChainingSignature.cjs');
34
+ var getSessionPublicKey = require('../../../client/extension/functions/getSessionPublicKey/getSessionPublicKey.cjs');
34
35
  var apiUrl = require('../../constants/waas/apiUrl.cjs');
35
36
  require('@dynamic-labs/locale');
36
37
  var dynamicContextProps = require('../../../store/state/dynamicContextProps/dynamicContextProps.cjs');
@@ -118,7 +119,11 @@ require('../../../store/state/multichainBalances.cjs');
118
119
  require('@dynamic-labs/store');
119
120
  require('../../../shared/utils/functions/getInitialUrl/getInitialUrl.cjs');
120
121
 
121
- const useSetWalletConnectorFetchers = (walletOptions, environmentId, projectSettings) => {
122
+ const useSetWalletConnectorFetchers = (walletOptions, environmentId, projectSettings,
123
+ // Logs the user out when the iframe reports a 401. Passed in (rather than read
124
+ // from context) because this hook runs inside DynamicContextProvider itself,
125
+ // where the context value isn't available to consume.
126
+ onUnauthorized) => {
122
127
  var _a, _b, _c;
123
128
  const apiBaseUrl = dynamicContextProps.useApiBaseUrl();
124
129
  const { registerEmbeddedWalletSessionKey, removeSessionKey } = useEmbeddedWalletSessionKeys.useEmbeddedWalletSessionKeys({ environmentId, projectSettings });
@@ -141,8 +146,10 @@ const useSetWalletConnectorFetchers = (walletOptions, environmentId, projectSett
141
146
  authMode: isCookieAuthEnabled ? 'cookie' : 'header',
142
147
  environmentId,
143
148
  getMfaToken,
149
+ getSessionPublicKey: getSessionPublicKey.getSessionPublicKey,
144
150
  getSignedSessionId: generateChainingSignature.generateChainingSignature,
145
151
  getSvmGasSponsorshipEnabled: () => svmGasSponsorshipEnabledRef.current,
152
+ onUnauthorized,
146
153
  relayUrl: (_e = (_d = (_c = projectSettings === null || projectSettings === void 0 ? void 0 : projectSettings.sdk) === null || _c === void 0 ? void 0 : _c.waas) === null || _d === void 0 ? void 0 : _d.relayUrl) !== null && _e !== void 0 ? _e : '',
147
154
  walletConnector: walletOption.walletConnector,
148
155
  });
@@ -1,3 +1,3 @@
1
1
  import { ProjectSettings } from '@dynamic-labs/sdk-api-core';
2
2
  import { WalletOption } from '../../../shared';
3
- export declare const useSetWalletConnectorFetchers: (walletOptions: WalletOption[] | undefined, environmentId: string, projectSettings: ProjectSettings | undefined) => void;
3
+ export declare const useSetWalletConnectorFetchers: (walletOptions: WalletOption[] | undefined, environmentId: string, projectSettings: ProjectSettings | undefined, onUnauthorized?: () => void | Promise<void>) => void;
@@ -27,6 +27,7 @@ import '@dynamic-labs-sdk/client';
27
27
  import '../../../config/ApiEndpoint.js';
28
28
  import { isCookieEnabled } from '../../../client/extension/functions/isCookieEnabled/isCookieEnabled.js';
29
29
  import { generateChainingSignature } from '../../../client/extension/functions/generateChainingSignature/generateChainingSignature.js';
30
+ import { getSessionPublicKey } from '../../../client/extension/functions/getSessionPublicKey/getSessionPublicKey.js';
30
31
  import { DEFAULT_WAAS_API_URL } from '../../constants/waas/apiUrl.js';
31
32
  import '@dynamic-labs/locale';
32
33
  import { useApiBaseUrl } from '../../../store/state/dynamicContextProps/dynamicContextProps.js';
@@ -114,7 +115,11 @@ import '../../../store/state/multichainBalances.js';
114
115
  import '@dynamic-labs/store';
115
116
  import '../../../shared/utils/functions/getInitialUrl/getInitialUrl.js';
116
117
 
117
- const useSetWalletConnectorFetchers = (walletOptions, environmentId, projectSettings) => {
118
+ const useSetWalletConnectorFetchers = (walletOptions, environmentId, projectSettings,
119
+ // Logs the user out when the iframe reports a 401. Passed in (rather than read
120
+ // from context) because this hook runs inside DynamicContextProvider itself,
121
+ // where the context value isn't available to consume.
122
+ onUnauthorized) => {
118
123
  var _a, _b, _c;
119
124
  const apiBaseUrl = useApiBaseUrl();
120
125
  const { registerEmbeddedWalletSessionKey, removeSessionKey } = useEmbeddedWalletSessionKeys({ environmentId, projectSettings });
@@ -137,8 +142,10 @@ const useSetWalletConnectorFetchers = (walletOptions, environmentId, projectSett
137
142
  authMode: isCookieAuthEnabled ? 'cookie' : 'header',
138
143
  environmentId,
139
144
  getMfaToken,
145
+ getSessionPublicKey,
140
146
  getSignedSessionId: generateChainingSignature,
141
147
  getSvmGasSponsorshipEnabled: () => svmGasSponsorshipEnabledRef.current,
148
+ onUnauthorized,
142
149
  relayUrl: (_e = (_d = (_c = projectSettings === null || projectSettings === void 0 ? void 0 : projectSettings.sdk) === null || _c === void 0 ? void 0 : _c.waas) === null || _d === void 0 ? void 0 : _d.relayUrl) !== null && _e !== void 0 ? _e : '',
143
150
  walletConnector: walletOption.walletConnector,
144
151
  });
@@ -57,6 +57,7 @@ const isPolicyViolationError = (error) => {
57
57
  (message === null || message === void 0 ? void 0 : message.toLowerCase().includes('explicitly denied by policy')));
58
58
  };
59
59
  const transactionErrorMessage = (err) => {
60
+ var _a, _b;
60
61
  let error = err;
61
62
  if (isTransactionExecutionError(err)) {
62
63
  error = err.walk();
@@ -84,6 +85,13 @@ const transactionErrorMessage = (err) => {
84
85
  if (isConfirmationTimeoutError(error)) {
85
86
  return undefined; // Use translation key in UI for timeout message
86
87
  }
88
+ // Midnight: every transaction needs DUST to pay fees. The wallet client throws
89
+ // "No dust balance — register dust first" when the wallet has no registered
90
+ // dust, so surface an actionable message instead of the generic fallback.
91
+ const message = (_b = (_a = getMessage(error)) === null || _a === void 0 ? void 0 : _a.toLowerCase()) !== null && _b !== void 0 ? _b : '';
92
+ if (message.includes('no dust') || message.includes('register dust')) {
93
+ return 'You need DUST to pay network fees. Register DUST first, then try again.';
94
+ }
87
95
  return 'Something went wrong with the transaction. Please try again.';
88
96
  };
89
97
  const isMagicInsufficientFundsError = (error) => {
@@ -53,6 +53,7 @@ const isPolicyViolationError = (error) => {
53
53
  (message === null || message === void 0 ? void 0 : message.toLowerCase().includes('explicitly denied by policy')));
54
54
  };
55
55
  const transactionErrorMessage = (err) => {
56
+ var _a, _b;
56
57
  let error = err;
57
58
  if (isTransactionExecutionError(err)) {
58
59
  error = err.walk();
@@ -80,6 +81,13 @@ const transactionErrorMessage = (err) => {
80
81
  if (isConfirmationTimeoutError(error)) {
81
82
  return undefined; // Use translation key in UI for timeout message
82
83
  }
84
+ // Midnight: every transaction needs DUST to pay fees. The wallet client throws
85
+ // "No dust balance — register dust first" when the wallet has no registered
86
+ // dust, so surface an actionable message instead of the generic fallback.
87
+ const message = (_b = (_a = getMessage(error)) === null || _a === void 0 ? void 0 : _a.toLowerCase()) !== null && _b !== void 0 ? _b : '';
88
+ if (message.includes('no dust') || message.includes('register dust')) {
89
+ return 'You need DUST to pay network fees. Register DUST first, then try again.';
90
+ }
83
91
  return 'Something went wrong with the transaction. Please try again.';
84
92
  };
85
93
  const isMagicInsufficientFundsError = (error) => {
@@ -8,6 +8,7 @@ var jsxRuntime = require('react/jsx-runtime');
8
8
  var React = require('react');
9
9
  var reactI18next = require('react-i18next');
10
10
  var walletConnectorCore = require('@dynamic-labs/wallet-connector-core');
11
+ var Button = require('../../../../components/Button/Button.cjs');
11
12
  var Skeleton = require('../../../../components/Skeleton/Skeleton.cjs');
12
13
  require('../../../../context/DynamicContext/useDynamicContext/useDynamicContext.cjs');
13
14
  var useInternalDynamicContext = require('../../../../context/DynamicContext/useDynamicContext/useInternalDynamicContext/useInternalDynamicContext.cjs');
@@ -26,6 +27,9 @@ var classNames = require('../../../../utils/functions/classNames/classNames.cjs'
26
27
  var usePromise = require('../../../../utils/hooks/usePromise/usePromise.cjs');
27
28
 
28
29
  const DUST_SEGMENTS = 20;
30
+ // Stable keys for the fixed-length decorative dust bar so the segments don't
31
+ // rely on the array index as their React key.
32
+ const DUST_SEGMENT_KEYS = Array.from({ length: DUST_SEGMENTS }, (_, i) => `dust-segment-${i}`);
29
33
  const truncateToDecimals = (value, decimals) => {
30
34
  const [whole, fractional] = value.split('.');
31
35
  if (!fractional || decimals <= 0)
@@ -61,6 +65,10 @@ const ActiveMidnightWalletBalance = ({ isLoading = false, }) => {
61
65
  const [activeTab, setActiveTab] = React.useState('shielded');
62
66
  const walletAddress = primaryWallet === null || primaryWallet === void 0 ? void 0 : primaryWallet.address;
63
67
  const enabled = Boolean(!isLoading && primaryWallet && walletConnectorCore.isMidnightConnector(primaryWallet.connector));
68
+ // Balance reads resolve on shielded+unshielded; dust finishes in the
69
+ // background. While dust is still syncing we re-fetch on an interval so the
70
+ // dust gauge fills in once it's ready (shielded/unshielded already final).
71
+ const [dustPollTick, setDustPollTick] = React.useState(0);
64
72
  const { data: balances, isLoading: balancesLoading } = usePromise.usePromise(() => _tslib.__awaiter(void 0, void 0, void 0, function* () {
65
73
  if (!primaryWallet ||
66
74
  !walletConnectorCore.isMidnightConnector(primaryWallet.connector) ||
@@ -69,11 +77,31 @@ const ActiveMidnightWalletBalance = ({ isLoading = false, }) => {
69
77
  }
70
78
  return primaryWallet.getFormattedBalances();
71
79
  }), {
72
- deps: [walletAddress, primaryWallet],
80
+ deps: [walletAddress, primaryWallet, dustPollTick],
73
81
  enabled,
74
82
  });
75
- const { dustBalance, shieldedBalance, unshieldedBalance } = balances !== null && balances !== void 0 ? balances : {};
76
- if (isLoading || balancesLoading) {
83
+ const { dustBalance, shieldedBalance, unshieldedBalance, dustSyncing } = balances !== null && balances !== void 0 ? balances : {};
84
+ React.useEffect(() => {
85
+ if (!dustSyncing)
86
+ return undefined;
87
+ const id = setInterval(() => setDustPollTick((tick) => tick + 1), 15000);
88
+ return () => clearInterval(id);
89
+ }, [dustSyncing]);
90
+ // `registerDust` is a WaaS-only connector method (external/injected Midnight
91
+ // wallets manage dust themselves). When the embedded wallet has no dust
92
+ // registered yet, we surface a "Register DUST" button instead of the gauge.
93
+ const isWaasWallet = Boolean(primaryWallet && walletConnectorCore.isDynamicWaasConnector(primaryWallet.connector));
94
+ const handleRegisterDust = () => _tslib.__awaiter(void 0, void 0, void 0, function* () {
95
+ const connector = primaryWallet === null || primaryWallet === void 0 ? void 0 : primaryWallet.connector;
96
+ if (!(connector === null || connector === void 0 ? void 0 : connector.registerDust))
97
+ return;
98
+ yield connector.registerDust();
99
+ // Dust generation begins after registration; re-fetch so the gauge appears.
100
+ setDustPollTick((tick) => tick + 1);
101
+ });
102
+ // Only show the full skeleton on the very first load; background dust polls
103
+ // keep `balances` populated so the view never flashes back to a skeleton.
104
+ if ((isLoading || balancesLoading) && !balances) {
77
105
  return (jsxRuntime.jsx("div", { className: 'active-midnight-balance', children: jsxRuntime.jsx(Skeleton.Skeleton, { className: 'active-midnight-balance__skeleton' }) }));
78
106
  }
79
107
  const dustFill = dustBalance
@@ -86,16 +114,31 @@ const ActiveMidnightWalletBalance = ({ isLoading = false, }) => {
86
114
  }
87
115
  : undefined;
88
116
  const activeBalance = activeTab === 'shielded' ? shieldedBalance : unshieldedBalance;
89
- return (jsxRuntime.jsx("div", { className: 'active-midnight-balance', children: jsxRuntime.jsxs("div", { className: 'active-midnight-balance__card', children: [dustBalance && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsxs("div", { className: 'active-midnight-balance__dust', children: [jsxRuntime.jsxs("div", { className: 'active-midnight-balance__dust-header', children: [jsxRuntime.jsxs("div", { className: 'active-midnight-balance__dust-title', children: [jsxRuntime.jsx("svg", { className: 'active-midnight-balance__dust-icon', width: '12', height: '12', viewBox: '0 0 24 24', children: jsxRuntime.jsx("path", { d: 'M13 2L3 14h9l-1 8 10-12h-9l1-8z' }) }), jsxRuntime.jsx("span", { className: 'active-midnight-balance__dust-label', "data-copykey": 'dyn_active_wallet_info.midnight_dust_generating', children: t('dyn_active_wallet_info.midnight_dust_generating') })] }), jsxRuntime.jsxs("span", { className: 'active-midnight-balance__dust-count', children: [dustBalanceDisplay === null || dustBalanceDisplay === void 0 ? void 0 : dustBalanceDisplay.balance, "/", dustBalanceDisplay === null || dustBalanceDisplay === void 0 ? void 0 : dustBalanceDisplay.cap] })] }), jsxRuntime.jsx("div", { className: 'active-midnight-balance__dust-bar', children: Array.from({ length: DUST_SEGMENTS }, (_, i) => (jsxRuntime.jsx("div", { className: [
90
- 'active-midnight-balance__dust-segment',
91
- i === 0 && 'active-midnight-balance__dust-segment--first',
92
- i === DUST_SEGMENTS - 1 &&
93
- 'active-midnight-balance__dust-segment--last',
94
- i < dustFill &&
95
- 'active-midnight-balance__dust-segment--filled',
96
- ]
97
- .filter(Boolean)
98
- .join(' ') }, i))) })] }), jsxRuntime.jsx("div", { className: 'active-midnight-balance__divider' })] })), jsxRuntime.jsx("div", { className: 'active-midnight-balance__tab-bar', children: TABS.map(({ id, copykey }) => (jsxRuntime.jsx("button", { className: classNames.classNames('active-midnight-balance__tab', {
117
+ // Dust section has three mutually exclusive states (syncing, generating with
118
+ // a balance, and the WaaS "register dust" prompt). Resolved via a helper to
119
+ // avoid nesting ternaries in the JSX below.
120
+ const renderDustSection = () => {
121
+ if (dustSyncing) {
122
+ return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("div", { className: 'active-midnight-balance__dust', children: jsxRuntime.jsx("div", { className: 'active-midnight-balance__dust-header', children: jsxRuntime.jsxs("div", { className: 'active-midnight-balance__dust-title', children: [jsxRuntime.jsx("svg", { className: 'active-midnight-balance__dust-icon', width: '12', height: '12', viewBox: '0 0 24 24', children: jsxRuntime.jsx("path", { d: 'M13 2L3 14h9l-1 8 10-12h-9l1-8z' }) }), jsxRuntime.jsx("span", { className: 'active-midnight-balance__dust-label', "data-copykey": 'dyn_active_wallet_info.midnight_dust_syncing', children: t('dyn_active_wallet_info.midnight_dust_syncing') })] }) }) }), jsxRuntime.jsx("div", { className: 'active-midnight-balance__divider' })] }));
123
+ }
124
+ if (dustBalance) {
125
+ return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsxs("div", { className: 'active-midnight-balance__dust', children: [jsxRuntime.jsxs("div", { className: 'active-midnight-balance__dust-header', children: [jsxRuntime.jsxs("div", { className: 'active-midnight-balance__dust-title', children: [jsxRuntime.jsx("svg", { className: 'active-midnight-balance__dust-icon', width: '12', height: '12', viewBox: '0 0 24 24', children: jsxRuntime.jsx("path", { d: 'M13 2L3 14h9l-1 8 10-12h-9l1-8z' }) }), jsxRuntime.jsx("span", { className: 'active-midnight-balance__dust-label', "data-copykey": 'dyn_active_wallet_info.midnight_dust_generating', children: t('dyn_active_wallet_info.midnight_dust_generating') })] }), jsxRuntime.jsxs("span", { className: 'active-midnight-balance__dust-count', children: [dustBalanceDisplay === null || dustBalanceDisplay === void 0 ? void 0 : dustBalanceDisplay.balance, "/", dustBalanceDisplay === null || dustBalanceDisplay === void 0 ? void 0 : dustBalanceDisplay.cap] })] }), jsxRuntime.jsx("div", { className: 'active-midnight-balance__dust-bar', children: DUST_SEGMENT_KEYS.map((segmentKey, i) => (jsxRuntime.jsx("div", { className: [
126
+ 'active-midnight-balance__dust-segment',
127
+ i === 0 && 'active-midnight-balance__dust-segment--first',
128
+ i === DUST_SEGMENTS - 1 &&
129
+ 'active-midnight-balance__dust-segment--last',
130
+ i < dustFill &&
131
+ 'active-midnight-balance__dust-segment--filled',
132
+ ]
133
+ .filter(Boolean)
134
+ .join(' ') }, segmentKey))) })] }), jsxRuntime.jsx("div", { className: 'active-midnight-balance__divider' })] }));
135
+ }
136
+ if (isWaasWallet) {
137
+ return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsxs("div", { className: 'active-midnight-balance__dust', children: [jsxRuntime.jsx("div", { className: 'active-midnight-balance__dust-header', children: jsxRuntime.jsxs("div", { className: 'active-midnight-balance__dust-title', children: [jsxRuntime.jsx("svg", { className: 'active-midnight-balance__dust-icon', width: '12', height: '12', viewBox: '0 0 24 24', children: jsxRuntime.jsx("path", { d: 'M13 2L3 14h9l-1 8 10-12h-9l1-8z' }) }), jsxRuntime.jsx("span", { className: 'active-midnight-balance__dust-label', "data-copykey": 'dyn_active_wallet_info.midnight_dust_generating', children: t('dyn_active_wallet_info.midnight_dust_generating') })] }) }), jsxRuntime.jsx(Button.Button, { buttonPadding: 'small', buttonVariant: 'secondary', buttonClassName: 'active-midnight-balance__register-dust-button', onClick: handleRegisterDust, "data-copykey": 'dyn_active_wallet_info.midnight_register_dust', children: t('dyn_active_wallet_info.midnight_register_dust') })] }), jsxRuntime.jsx("div", { className: 'active-midnight-balance__divider' })] }));
138
+ }
139
+ return null;
140
+ };
141
+ return (jsxRuntime.jsx("div", { className: 'active-midnight-balance', children: jsxRuntime.jsxs("div", { className: 'active-midnight-balance__card', children: [renderDustSection(), jsxRuntime.jsx("div", { className: 'active-midnight-balance__tab-bar', children: TABS.map(({ id, copykey }) => (jsxRuntime.jsx("button", { className: classNames.classNames('active-midnight-balance__tab', {
99
142
  'active-midnight-balance__tab--active': activeTab === id,
100
143
  }), "data-copykey": copykey, onClick: () => setActiveTab(id), children: t(copykey) }, id))) }), jsxRuntime.jsxs("div", { className: 'active-midnight-balance__pocket', children: [activeTab === 'shielded' ? (jsxRuntime.jsx(midnightShielded.ReactComponent, { className: 'active-midnight-balance__pocket-icon' })) : (jsxRuntime.jsx(midnightUnshielded.ReactComponent, { className: 'active-midnight-balance__pocket-icon' })), jsxRuntime.jsxs("span", { className: 'active-midnight-balance__amount', children: [activeBalance !== null && activeBalance !== void 0 ? activeBalance : '—', " NIGHT"] })] })] }) }));
101
144
  };