@dynamic-labs/sdk-react-core 4.79.2 → 4.81.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 (101) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/package.cjs +2 -2
  3. package/package.js +2 -2
  4. package/package.json +13 -13
  5. package/src/lib/Main.cjs +2 -2
  6. package/src/lib/Main.js +2 -2
  7. package/src/lib/client/extension/deprecated/mfa/verifyTotpMfaDevice/verifyTotpMfaDevice.d.ts +1 -1
  8. package/src/lib/components/LogoutButton/LogoutButton.cjs +2 -2
  9. package/src/lib/components/LogoutButton/LogoutButton.js +2 -2
  10. package/src/lib/components/SendBalanceForm/SendBalanceForm.cjs +63 -3
  11. package/src/lib/components/SendBalanceForm/SendBalanceForm.js +63 -3
  12. package/src/lib/components/SendBalanceForm/TransactionModeSegmentedControl/TransactionModeSegmentedControl.cjs +40 -0
  13. package/src/lib/components/SendBalanceForm/TransactionModeSegmentedControl/TransactionModeSegmentedControl.d.ts +16 -0
  14. package/src/lib/components/SendBalanceForm/TransactionModeSegmentedControl/TransactionModeSegmentedControl.js +36 -0
  15. package/src/lib/components/SendBalanceForm/TransactionModeSegmentedControl/icons.cjs +17 -0
  16. package/src/lib/components/SendBalanceForm/TransactionModeSegmentedControl/icons.d.ts +8 -0
  17. package/src/lib/components/SendBalanceForm/TransactionModeSegmentedControl/icons.js +12 -0
  18. package/src/lib/components/SendBalanceForm/TransactionModeSegmentedControl/index.d.ts +1 -0
  19. package/src/lib/context/DynamicContext/DynamicContext.cjs +6 -4
  20. package/src/lib/context/DynamicContext/DynamicContext.js +6 -4
  21. package/src/lib/context/DynamicContext/hooks/useHandleLogout/index.d.ts +1 -0
  22. package/src/lib/context/DynamicContext/hooks/useHandleLogout/types.d.ts +9 -0
  23. package/src/lib/context/DynamicContext/hooks/useHandleLogout/useHandleLogout.cjs +25 -12
  24. package/src/lib/context/DynamicContext/hooks/useHandleLogout/useHandleLogout.d.ts +5 -3
  25. package/src/lib/context/DynamicContext/hooks/useHandleLogout/useHandleLogout.js +24 -11
  26. package/src/lib/context/DynamicContext/hooks/useRemoveWallet/useRemoveWallet.cjs +3 -3
  27. package/src/lib/context/DynamicContext/hooks/useRemoveWallet/useRemoveWallet.d.ts +3 -2
  28. package/src/lib/context/DynamicContext/hooks/useRemoveWallet/useRemoveWallet.js +3 -3
  29. package/src/lib/context/DynamicContext/types/IDynamicContext.d.ts +2 -0
  30. package/src/lib/context/DynamicContext/types/IInternalDynamicContext.d.ts +8 -1
  31. package/src/lib/events/auth.d.ts +2 -1
  32. package/src/lib/styles/index.shadow.cjs +1 -1
  33. package/src/lib/styles/index.shadow.js +1 -1
  34. package/src/lib/utils/hooks/useAleoShieldedBalances/index.d.ts +1 -0
  35. package/src/lib/utils/hooks/useAleoShieldedBalances/useAleoShieldedBalances.cjs +372 -0
  36. package/src/lib/utils/hooks/useAleoShieldedBalances/useAleoShieldedBalances.d.ts +24 -0
  37. package/src/lib/utils/hooks/useAleoShieldedBalances/useAleoShieldedBalances.js +368 -0
  38. package/src/lib/utils/hooks/useDeleteUserAccount/useDeleteUserAccount.cjs +2 -2
  39. package/src/lib/utils/hooks/useDeleteUserAccount/useDeleteUserAccount.js +2 -2
  40. package/src/lib/utils/hooks/useEmbeddedWallet/useEmbeddedWallet.cjs +1 -0
  41. package/src/lib/utils/hooks/useEmbeddedWallet/useEmbeddedWallet.d.ts +1 -0
  42. package/src/lib/utils/hooks/useEmbeddedWallet/useEmbeddedWallet.js +1 -0
  43. package/src/lib/utils/hooks/useEmbeddedWalletSessionKeys/useEmbeddedWalletSessionKeys.cjs +1 -1
  44. package/src/lib/utils/hooks/useEmbeddedWalletSessionKeys/useEmbeddedWalletSessionKeys.js +1 -1
  45. package/src/lib/utils/hooks/useUserAuth/useUserAuth.cjs +3 -3
  46. package/src/lib/utils/hooks/useUserAuth/useUserAuth.js +3 -3
  47. package/src/lib/utils/hooks/useValidateSession/handleStoreAndEnvironmentMismatch/handleStoreAndEnvironmentMismatch.cjs +1 -1
  48. package/src/lib/utils/hooks/useValidateSession/handleStoreAndEnvironmentMismatch/handleStoreAndEnvironmentMismatch.d.ts +2 -1
  49. package/src/lib/utils/hooks/useValidateSession/handleStoreAndEnvironmentMismatch/handleStoreAndEnvironmentMismatch.js +1 -1
  50. package/src/lib/utils/hooks/useValidateSession/handleWalletInfoOutOfSync/handleWalletInfoOutOfSync.cjs +3 -1
  51. package/src/lib/utils/hooks/useValidateSession/handleWalletInfoOutOfSync/handleWalletInfoOutOfSync.d.ts +2 -2
  52. package/src/lib/utils/hooks/useValidateSession/handleWalletInfoOutOfSync/handleWalletInfoOutOfSync.js +3 -1
  53. package/src/lib/utils/hooks/useValidateSession/useValidateSession.cjs +3 -3
  54. package/src/lib/utils/hooks/useValidateSession/useValidateSession.d.ts +3 -3
  55. package/src/lib/utils/hooks/useValidateSession/useValidateSession.js +3 -3
  56. package/src/lib/utils/hooks/useWalletEventListeners/useWalletEventListeners.cjs +2 -2
  57. package/src/lib/utils/hooks/useWalletEventListeners/useWalletEventListeners.d.ts +3 -2
  58. package/src/lib/utils/hooks/useWalletEventListeners/useWalletEventListeners.js +2 -2
  59. package/src/lib/views/BackupUnsuccessfulView/BackupUnsuccessfulView.cjs +5 -5
  60. package/src/lib/views/BackupUnsuccessfulView/BackupUnsuccessfulView.js +5 -5
  61. package/src/lib/views/CollectUserDataView/CollectUserDataView.cjs +2 -2
  62. package/src/lib/views/CollectUserDataView/CollectUserDataView.js +2 -2
  63. package/src/lib/views/CollectUserDataViewNoWallet/CollectUserDataViewNoWallet.cjs +2 -2
  64. package/src/lib/views/CollectUserDataViewNoWallet/CollectUserDataViewNoWallet.js +2 -2
  65. package/src/lib/views/DeviceRegistrationView/DeviceRegistrationView.cjs +2 -2
  66. package/src/lib/views/DeviceRegistrationView/DeviceRegistrationView.js +2 -2
  67. package/src/lib/views/EmailVerification/EmailVerification.cjs +2 -2
  68. package/src/lib/views/EmailVerification/EmailVerification.js +2 -2
  69. package/src/lib/views/EmbeddedDeleteView/EmbeddedDeleteView.cjs +2 -2
  70. package/src/lib/views/EmbeddedDeleteView/EmbeddedDeleteView.js +2 -2
  71. package/src/lib/views/MfaChooseDeviceView/MfaChooseDeviceView.cjs +7 -7
  72. package/src/lib/views/MfaChooseDeviceView/MfaChooseDeviceView.js +7 -7
  73. package/src/lib/views/MfaSecureDeviceView/MfaSecureDeviceView.cjs +7 -7
  74. package/src/lib/views/MfaSecureDeviceView/MfaSecureDeviceView.js +7 -7
  75. package/src/lib/views/MfaVerificationView/MfaVerificationView.cjs +5 -5
  76. package/src/lib/views/MfaVerificationView/MfaVerificationView.js +5 -5
  77. package/src/lib/views/Passkey/PasskeyIntroView/PasskeyIntroView.cjs +2 -2
  78. package/src/lib/views/Passkey/PasskeyIntroView/PasskeyIntroView.js +2 -2
  79. package/src/lib/views/SendBalanceView/SendBalanceView.cjs +53 -0
  80. package/src/lib/views/SendBalanceView/SendBalanceView.js +53 -0
  81. package/src/lib/views/SmsVerification/SmsVerification.cjs +2 -2
  82. package/src/lib/views/SmsVerification/SmsVerification.js +2 -2
  83. package/src/lib/views/WalletDelegation/WalletDelegationView/WalletDelegationView.cjs +2 -2
  84. package/src/lib/views/WalletDelegation/WalletDelegationView/WalletDelegationView.js +2 -2
  85. package/src/lib/views/WalletLockedView/WalletLockedView.cjs +2 -2
  86. package/src/lib/views/WalletLockedView/WalletLockedView.js +2 -2
  87. package/src/lib/views/WalletUsedView/WalletUsedView.cjs +2 -2
  88. package/src/lib/views/WalletUsedView/WalletUsedView.js +2 -2
  89. package/src/lib/widgets/DynamicWidget/components/ActiveWalletBalance/ActiveWalletBalance.cjs +191 -11
  90. package/src/lib/widgets/DynamicWidget/components/ActiveWalletBalance/ActiveWalletBalance.js +191 -11
  91. package/src/lib/widgets/DynamicWidget/components/ActiveWalletBalance/TokenBalanceItem/TokenBalanceItem.cjs +5 -2
  92. package/src/lib/widgets/DynamicWidget/components/ActiveWalletBalance/TokenBalanceItem/TokenBalanceItem.d.ts +10 -1
  93. package/src/lib/widgets/DynamicWidget/components/ActiveWalletBalance/TokenBalanceItem/TokenBalanceItem.js +5 -2
  94. package/src/lib/widgets/DynamicWidget/components/ActiveWalletBalance/TokenBalanceItem/index.d.ts +1 -0
  95. package/src/lib/widgets/DynamicWidget/components/ActiveWalletBalance/TokenBalanceList/TokenBalanceList.cjs +2 -2
  96. package/src/lib/widgets/DynamicWidget/components/ActiveWalletBalance/TokenBalanceList/TokenBalanceList.d.ts +3 -1
  97. package/src/lib/widgets/DynamicWidget/components/ActiveWalletBalance/TokenBalanceList/TokenBalanceList.js +2 -2
  98. package/src/lib/widgets/DynamicWidget/components/SingleWalletButtons/SingleWalletButtons.cjs +2 -2
  99. package/src/lib/widgets/DynamicWidget/components/SingleWalletButtons/SingleWalletButtons.js +2 -2
  100. package/src/lib/widgets/DynamicWidget/views/SettingsView/SettingsView.cjs +2 -2
  101. package/src/lib/widgets/DynamicWidget/views/SettingsView/SettingsView.js +2 -2
@@ -15,13 +15,13 @@ var check = require('../../../../shared/assets/check.cjs');
15
15
  var chevronDown = require('../../../../shared/assets/chevron-down.cjs');
16
16
  var reloadIcon = require('../../../../shared/assets/reload-icon.cjs');
17
17
  require('../../../../context/ViewContext/ViewContext.cjs');
18
- require('../../../../shared/logger.cjs');
18
+ var logger = require('../../../../shared/logger.cjs');
19
19
  require('@dynamic-labs/wallet-book');
20
20
  require('@dynamic-labs/utils');
21
21
  require('../../../../utils/constants/colors.cjs');
22
22
  require('../../../../utils/constants/values.cjs');
23
23
  require('@dynamic-labs/sdk-api-core');
24
- require('../../../../shared/consts/index.cjs');
24
+ var index = require('../../../../shared/consts/index.cjs');
25
25
  require('../../../../events/dynamicEvents.cjs');
26
26
  require('../../../../context/CaptchaContext/CaptchaContext.cjs');
27
27
  require('../../../../context/ErrorContext/ErrorContext.cjs');
@@ -63,6 +63,7 @@ require('../../../../context/UserFieldEditorContext/UserFieldEditorContext.cjs')
63
63
  require('@dynamic-labs/rpc-providers');
64
64
  require('../../../../store/state/walletOptions/walletOptions.cjs');
65
65
  var AccordionItem = require('../../../../components/Accordion/components/AccordionItem/AccordionItem.cjs');
66
+ var Button = require('../../../../components/Button/Button.cjs');
66
67
  require('../../../../components/Alert/Alert.cjs');
67
68
  var Typography = require('../../../../components/Typography/Typography.cjs');
68
69
  require('../../../../components/ShadowDOM/ShadowDOM.cjs');
@@ -72,13 +73,15 @@ require('../../../../components/Input/Input.cjs');
72
73
  require('../../../../components/IsBrowser/IsBrowser.cjs');
73
74
  require('../../../../components/MenuList/Dropdown/Dropdown.cjs');
74
75
  require('../../../../components/OverlayCard/OverlayCard.cjs');
76
+ var Modal = require('../../../../components/Modal/Modal.cjs');
77
+ var ModalCard = require('../../../../components/ModalCard/ModalCard.cjs');
75
78
  require('../../../../components/Transition/ZoomTransition/ZoomTransition.cjs');
76
79
  require('../../../../components/Transition/SlideInUpTransition/SlideInUpTransition.cjs');
77
80
  require('../../../../components/Transition/OpacityTransition/OpacityTransition.cjs');
78
81
  require('../../../../components/PasskeyCreatedSuccessBanner/PasskeyCreatedSuccessBanner.cjs');
79
82
  require('../../../../components/Popper/Popper/Popper.cjs');
80
83
  require('../../../../components/Popper/PopperContext/PopperContext.cjs');
81
- require('react-focus-lock');
84
+ var Portal = require('../../../../components/Portal/Portal.cjs');
82
85
  require('qrcode');
83
86
  require('formik');
84
87
  require('../../../../utils/hooks/useSubdomainCheck/useSubdomainCheck.cjs');
@@ -117,11 +120,12 @@ require('../../../../store/state/multichainBalances.cjs');
117
120
  require('@dynamic-labs/store');
118
121
  require('../../../../shared/utils/functions/getInitialUrl/getInitialUrl.cjs');
119
122
  var useInternalDynamicContext = require('../../../../context/DynamicContext/useDynamicContext/useInternalDynamicContext/useInternalDynamicContext.cjs');
123
+ var useAleoShieldedBalances = require('../../../../utils/hooks/useAleoShieldedBalances/useAleoShieldedBalances.cjs');
120
124
  var TokenBalanceList = require('./TokenBalanceList/TokenBalanceList.cjs');
121
125
 
122
126
  /** Component to display token balances for the primary wallet */
123
127
  const ActiveWalletBalance = ({ isLoading = false, }) => {
124
- var _a, _b, _c;
128
+ var _a, _b, _c, _d, _e;
125
129
  const { t } = reactI18next.useTranslation();
126
130
  /** Controls for the multi asset balance accordion */
127
131
  const [balanceIsExpanded, setBalanceIsExpanded] = React.useState(false);
@@ -137,11 +141,132 @@ const ActiveWalletBalance = ({ isLoading = false, }) => {
137
141
  const authMode$1 = authMode.useAuthMode();
138
142
  const projectSettings = useProjectSettings.useProjectSettings();
139
143
  const { data: testnet } = usePromise.usePromise(() => _tslib.__awaiter(void 0, void 0, void 0, function* () { return Boolean(yield (primaryWallet === null || primaryWallet === void 0 ? void 0 : primaryWallet.connector.isTestnet())); }), { deps: [network], initialData: false });
140
- const { isLoading: isLoadingTokenBalances, tokenBalances, error: errorTokenBalances, fetchAccountBalances, } = useTokenBalances.useTokenBalances({
144
+ const { isLoading: isLoadingTokenBalances, tokenBalances: unshieldedTokenBalances, error: errorTokenBalances, fetchAccountBalances, } = useTokenBalances.useTokenBalances({
141
145
  chainName: primaryWallet === null || primaryWallet === void 0 ? void 0 : primaryWallet.connector.connectedChain,
142
146
  includeFiat: showFiat || !hasNativeToken,
143
147
  includeNativeBalance: true,
144
148
  });
149
+ const { tokenBalances: shieldedTokenBalances, isLoading: isLoadingShielded, refetch: refetchShielded, supportsShielded, } = useAleoShieldedBalances.useAleoShieldedBalances();
150
+ /**
151
+ * Tab state for chains that have a shielded/unshielded split. Defaults to
152
+ * shielded so Aleo wallets land on their primary balance type. Other
153
+ * chains render unchanged: when `supportsShielded` is false, the
154
+ * `tokenBalances` selector below short-circuits to unshielded
155
+ * regardless of this state, so the default has no visible effect for
156
+ * non-Aleo wallets. Only Aleo wallets see the toggle (gated by
157
+ * `supportsShielded`); they should land on Shielded since that's the
158
+ * privacy-first default for Aleo.
159
+ */
160
+ const [activeShieldTab, setActiveShieldTab] = React.useState('shielded');
161
+ /**
162
+ * Address of the token currently being shielded (during a click → broadcast
163
+ * cycle on the Unshielded tab's "Shield Manually" CTA). Used to flip that
164
+ * row's button to a `Working…` disabled state. Single-flight: one shield in
165
+ * flight at a time across the entire list — the form-level UX would get
166
+ * confusing otherwise (user-paid fees + multiple in-flight DPS proves).
167
+ */
168
+ const [shieldingAddress, setShieldingAddress] = React.useState(undefined);
169
+ // Duck-typed handle to the Aleo connector's shield helpers — only present
170
+ // on `DynamicWaasAleoConnector`. We deliberately avoid an `instanceof`
171
+ // check so this file doesn't pull a hard dependency on the Aleo package.
172
+ const aleoShieldHandle = React.useMemo(() => {
173
+ const c = primaryWallet === null || primaryWallet === void 0 ? void 0 : primaryWallet.connector;
174
+ if (c &&
175
+ typeof c.canShieldToken === 'function' &&
176
+ typeof c.shieldToken === 'function') {
177
+ return c;
178
+ }
179
+ return undefined;
180
+ }, [primaryWallet === null || primaryWallet === void 0 ? void 0 : primaryWallet.connector]);
181
+ // Token currently awaiting user-paid fee confirmation. Set when
182
+ // Shield Manually is clicked on a token Feemaster doesn't sponsor;
183
+ // cleared on Cancel or after the modal's Shield button dispatches.
184
+ const [pendingShieldToken, setPendingShieldToken] = React.useState(null);
185
+ const handleShieldToken = React.useCallback((token) => _tslib.__awaiter(void 0, void 0, void 0, function* () {
186
+ var _f;
187
+ if (!aleoShieldHandle || shieldingAddress)
188
+ return;
189
+ // `rawBalance` is the atomic-units count redcoast surfaces (number).
190
+ // Convert to bigint via Math.round to absorb any float drift from a
191
+ // decimal-shifted display value. <=0 entries get filtered out before
192
+ // the CTA renders, but the guard here is a safety net.
193
+ const atomic = BigInt(Math.round((_f = token.rawBalance) !== null && _f !== void 0 ? _f : 0));
194
+ if (atomic <= BigInt(0))
195
+ return;
196
+ setShieldingAddress(token.address);
197
+ try {
198
+ yield aleoShieldHandle.shieldToken({
199
+ amount: atomic,
200
+ isNative: token.isNative,
201
+ tokenAddress: token.address,
202
+ });
203
+ // Refresh the unshielded list so the just-shielded balance drops to
204
+ // 0 in the redcoast feed. The shielded side will pick the new
205
+ // record up on its own polling once the RecordScanner indexes.
206
+ yield fetchAccountBalances(true);
207
+ yield refetchShielded();
208
+ }
209
+ catch (err) {
210
+ logger.logger.debug('[ActiveWalletBalance] shieldToken failed', err);
211
+ }
212
+ finally {
213
+ setShieldingAddress(undefined);
214
+ }
215
+ }), [aleoShieldHandle, fetchAccountBalances, refetchShielded, shieldingAddress]);
216
+ const getSecondaryAction = React.useCallback((token) => {
217
+ // Only on the Unshielded tab, only when the connector exposes shield
218
+ // helpers, only for tokens registered as shieldable, only for tokens
219
+ // with a non-zero unshielded balance.
220
+ if (activeShieldTab !== 'unshielded')
221
+ return undefined;
222
+ if (!aleoShieldHandle)
223
+ return undefined;
224
+ if (!aleoShieldHandle.canShieldToken({
225
+ address: token.address,
226
+ isNative: token.isNative,
227
+ }))
228
+ return undefined;
229
+ if (!token.rawBalance || token.rawBalance <= 0)
230
+ return undefined;
231
+ return {
232
+ dataTestId: `shield-manually-${token.symbol}`,
233
+ isLoading: shieldingAddress === token.address,
234
+ label: 'Shield Manually',
235
+ onClick: () => {
236
+ // Pre-check Feemaster sponsorship — if covered, dispatch the
237
+ // shield silently (current behaviour). If not covered (e.g.
238
+ // policy gap, quota exhausted, lookup error), open a user-paid
239
+ // confirmation modal so the user knows they'll pay an ALEO
240
+ // network fee before we sign anything. The connector resolves
241
+ // the registry token's programId internally.
242
+ (() => _tslib.__awaiter(void 0, void 0, void 0, function* () {
243
+ var _a;
244
+ const sponsored = yield ((_a = aleoShieldHandle.isShieldSponsored) === null || _a === void 0 ? void 0 : _a.call(aleoShieldHandle, {
245
+ address: token.address,
246
+ isNative: token.isNative,
247
+ }));
248
+ if (sponsored) {
249
+ handleShieldToken(token).catch(() => {
250
+ /* error surfaced via state inside handleShieldToken */
251
+ });
252
+ }
253
+ else {
254
+ setPendingShieldToken(token);
255
+ }
256
+ }))().catch(() => {
257
+ /* error surfaced via state inside the callback */
258
+ });
259
+ },
260
+ };
261
+ }, [activeShieldTab, aleoShieldHandle, handleShieldToken, shieldingAddress]);
262
+ const tokenBalances = React.useMemo(() => supportsShielded && activeShieldTab === 'shielded'
263
+ ? shieldedTokenBalances
264
+ : unshieldedTokenBalances, [
265
+ activeShieldTab,
266
+ shieldedTokenBalances,
267
+ supportsShielded,
268
+ unshieldedTokenBalances,
269
+ ]);
145
270
  const filteredTokenBalances = React.useMemo(() => (tokenBalances === null || tokenBalances === void 0 ? void 0 : tokenBalances.filter((token) => token.name)) || [], [tokenBalances]);
146
271
  const totalValue = React.useMemo(() => filteredTokenBalances.reduce((acc, token) => acc + ((token === null || token === void 0 ? void 0 : token.marketValue) || 0), 0), [filteredTokenBalances]);
147
272
  const enableMultiAsset = React.useMemo(() => {
@@ -183,7 +308,12 @@ const ActiveWalletBalance = ({ isLoading = false, }) => {
183
308
  setIsRefreshing(true);
184
309
  setIsSuccess(false);
185
310
  try {
186
- yield fetchAccountBalances(true);
311
+ if (supportsShielded && activeShieldTab === 'shielded') {
312
+ yield refetchShielded();
313
+ }
314
+ else {
315
+ yield fetchAccountBalances(true);
316
+ }
187
317
  setIsRefreshing(false);
188
318
  setIsSuccess(true);
189
319
  successTimeoutRef.current = setTimeout(() => {
@@ -197,9 +327,19 @@ const ActiveWalletBalance = ({ isLoading = false, }) => {
197
327
  refreshTimeoutRef.current = null;
198
328
  }, 1000);
199
329
  }
200
- }), [isRefreshing, isSuccess, fetchAccountBalances]);
330
+ }), [
331
+ isRefreshing,
332
+ isSuccess,
333
+ fetchAccountBalances,
334
+ refetchShielded,
335
+ supportsShielded,
336
+ activeShieldTab,
337
+ ]);
201
338
  const primaryWalletNativeBalance = () => {
202
- if (!primaryWallet || isLoadingTokenBalances || isLoading) {
339
+ if (!primaryWallet ||
340
+ isLoadingTokenBalances ||
341
+ isLoading ||
342
+ (supportsShielded && activeShieldTab === 'shielded' && isLoadingShielded)) {
203
343
  return jsxRuntime.jsx(Skeleton.Skeleton, { className: 'balance-container__skeleton' });
204
344
  }
205
345
  // Chain has no native token - show aggregated fiat token values
@@ -212,7 +352,11 @@ const ActiveWalletBalance = ({ isLoading = false, }) => {
212
352
  ? currencyFormatter.format(parseFloat(totalValue.toFixed(2)))
213
353
  : '<$0.01' })) : (jsxRuntime.jsx(Balance.Balance, { className: 'balance-header__balance', wallet: primaryWallet, network: network, variant: 'numbers_display' }));
214
354
  };
215
- const balanceHeaderTitle = () => (jsxRuntime.jsxs("div", { className: 'balance-header__title', children: [isLoadingTokenBalances || isLoading ? (jsxRuntime.jsx(Skeleton.Skeleton, { className: 'balance-header__title-skeleton' })) : (jsxRuntime.jsx(Typography.Typography, { color: 'secondary', variant: 'body_normal', copykey: 'dyn_active_wallet_info.balance', children: t('dyn_active_wallet_info.balance') })), jsxRuntime.jsxs("div", { className: 'balance-header__balance', children: [primaryWalletNativeBalance(), isSuccess ? (jsxRuntime.jsx("div", { className: 'balance-header__success-icon', children: jsxRuntime.jsx(check.ReactComponent, { "data-testid": 'success-icon' }) })) : (jsxRuntime.jsx(reloadIcon.ReactComponent, { "data-testid": 'refresh-balances', className: `balance-header__refresh-icon ${isRefreshing ? 'balance-header__refresh-icon--spinning' : ''}`, onClick: handleRefresh, style: {
355
+ const balanceHeaderTitle = () => (jsxRuntime.jsxs("div", { className: 'balance-header__title', children: [isLoadingTokenBalances ||
356
+ isLoading ||
357
+ (supportsShielded &&
358
+ activeShieldTab === 'shielded' &&
359
+ isLoadingShielded) ? (jsxRuntime.jsx(Skeleton.Skeleton, { className: 'balance-header__title-skeleton' })) : (jsxRuntime.jsx(Typography.Typography, { color: 'secondary', variant: 'body_normal', copykey: 'dyn_active_wallet_info.balance', children: t('dyn_active_wallet_info.balance') })), jsxRuntime.jsxs("div", { className: 'balance-header__balance', children: [primaryWalletNativeBalance(), isSuccess ? (jsxRuntime.jsx("div", { className: 'balance-header__success-icon', children: jsxRuntime.jsx(check.ReactComponent, { "data-testid": 'success-icon' }) })) : (jsxRuntime.jsx(reloadIcon.ReactComponent, { "data-testid": 'refresh-balances', className: `balance-header__refresh-icon ${isRefreshing ? 'balance-header__refresh-icon--spinning' : ''}`, onClick: handleRefresh, style: {
216
360
  cursor: isRefreshing ? 'not-allowed' : 'pointer',
217
361
  opacity: isRefreshing ? 0.5 : 1,
218
362
  }, title: 'Refresh balances' }))] })] }));
@@ -224,8 +368,44 @@ const ActiveWalletBalance = ({ isLoading = false, }) => {
224
368
  if (!showMultiAsset) {
225
369
  return jsxRuntime.jsx("div", { className: 'balance-container', children: balanceHeaderTitle() });
226
370
  }
227
- return (jsxRuntime.jsx("div", { className: 'balance-container', children: jsxRuntime.jsxs("div", { className: 'multi-asset-balance-container', children: [jsxRuntime.jsx("div", { className: `${hasShadow ? 'shadow' : ''}`, children: jsxRuntime.jsxs("button", { onClick: toggleBalanceAccordion, className: 'balance-header', "data-testid": 'balance-header', children: [balanceHeaderTitle(), !isLoadingTokenBalances && (jsxRuntime.jsx("div", { className: balanceIsExpanded ? 'balance-header__chevron' : '', children: jsxRuntime.jsx(chevronDown.ReactComponent, {}) }))] }) }), jsxRuntime.jsx(AccordionItem.AccordionItem, { isOpen: balanceIsExpanded, className: `multi-asset-balance-container__accordion ${'multi-asset-balance-container__accordion' +
228
- (balanceIsExpanded ? '--expanded' : '--collapsed')}`, handleScroll: handleScroll, ref: contentRef, dataTestId: 'multi-asset-balance-accordion', children: jsxRuntime.jsx(TokenBalanceList.TokenBalanceList, { tokenBalances: filteredTokenBalances }) })] }) }));
371
+ return (jsxRuntime.jsxs("div", { className: 'balance-container', children: [jsxRuntime.jsxs("div", { className: 'multi-asset-balance-container', children: [jsxRuntime.jsx("div", { className: `${hasShadow ? 'shadow' : ''}`, children: jsxRuntime.jsxs("button", { onClick: toggleBalanceAccordion, className: 'balance-header', "data-testid": 'balance-header', children: [balanceHeaderTitle(), !isLoadingTokenBalances && (jsxRuntime.jsx("div", { className: balanceIsExpanded ? 'balance-header__chevron' : '', children: jsxRuntime.jsx(chevronDown.ReactComponent, {}) }))] }) }), jsxRuntime.jsxs(AccordionItem.AccordionItem, { isOpen: balanceIsExpanded, className: `multi-asset-balance-container__accordion ${'multi-asset-balance-container__accordion' +
372
+ (balanceIsExpanded ? '--expanded' : '--collapsed')}`, handleScroll: handleScroll, ref: contentRef, dataTestId: 'multi-asset-balance-accordion', children: [supportsShielded && (jsxRuntime.jsxs("div", { className: 'shield-tabs', "data-testid": 'shield-tabs', role: 'tablist', style: {
373
+ display: 'flex',
374
+ gap: 16,
375
+ padding: '12px 16px 8px 16px',
376
+ }, children: [jsxRuntime.jsx("button", { type: 'button', role: 'tab', "aria-selected": activeShieldTab === 'shielded', onClick: (e) => {
377
+ e.stopPropagation();
378
+ setActiveShieldTab('shielded');
379
+ }, style: {
380
+ background: 'none',
381
+ border: 'none',
382
+ cursor: 'pointer',
383
+ fontWeight: activeShieldTab === 'shielded' ? 600 : 400,
384
+ opacity: activeShieldTab === 'shielded' ? 1 : 0.5,
385
+ padding: 0,
386
+ }, children: jsxRuntime.jsx(Typography.Typography, { variant: 'body_normal', children: "Shielded" }) }), jsxRuntime.jsx("button", { type: 'button', role: 'tab', "aria-selected": activeShieldTab === 'unshielded', onClick: (e) => {
387
+ e.stopPropagation();
388
+ setActiveShieldTab('unshielded');
389
+ }, style: {
390
+ background: 'none',
391
+ border: 'none',
392
+ cursor: 'pointer',
393
+ fontWeight: activeShieldTab === 'unshielded' ? 600 : 400,
394
+ opacity: activeShieldTab === 'unshielded' ? 1 : 0.5,
395
+ padding: 0,
396
+ }, children: jsxRuntime.jsx(Typography.Typography, { variant: 'body_normal', children: "Unshielded" }) })] })), jsxRuntime.jsx(TokenBalanceList.TokenBalanceList, { tokenBalances: filteredTokenBalances, getSecondaryAction: getSecondaryAction })] })] }), jsxRuntime.jsx(Portal.Portal, { elementId: 'dynamic-shield-confirm', isShown: Boolean(pendingShieldToken), zIndex: index.authModalZIndex, withBackdrop: true, handleClose: () => setPendingShieldToken(null), children: jsxRuntime.jsx(Modal.Modal, { children: jsxRuntime.jsx(ModalCard.ModalCard, { children: jsxRuntime.jsxs("div", { style: { padding: 24 }, children: [jsxRuntime.jsxs(Typography.Typography, { variant: 'title', weight: 'medium', color: 'primary', children: ["Shield ", (_d = pendingShieldToken === null || pendingShieldToken === void 0 ? void 0 : pendingShieldToken.balance) !== null && _d !== void 0 ? _d : '', ' ', (_e = pendingShieldToken === null || pendingShieldToken === void 0 ? void 0 : pendingShieldToken.symbol) !== null && _e !== void 0 ? _e : ''] }), jsxRuntime.jsx(Typography.Typography, { variant: 'body_normal', color: 'secondary', style: { display: 'block', marginTop: 8 }, children: "This token doesn't support auto-shielding. A network fee is required to shield manually." }), jsxRuntime.jsxs("div", { style: {
397
+ display: 'flex',
398
+ gap: 8,
399
+ marginTop: 24,
400
+ }, children: [jsxRuntime.jsx(Button.Button, { buttonVariant: 'secondary', expanded: true, onClick: () => setPendingShieldToken(null), dataTestId: 'shield-confirm-modal-cancel', children: "Cancel" }), jsxRuntime.jsx(Button.Button, { buttonVariant: 'primary', expanded: true, onClick: () => {
401
+ const target = pendingShieldToken;
402
+ setPendingShieldToken(null);
403
+ if (target) {
404
+ handleShieldToken(target).catch(() => {
405
+ /* error surfaced via state inside handleShieldToken */
406
+ });
407
+ }
408
+ }, dataTestId: 'shield-confirm-modal-confirm', children: "Shield" })] })] }) }) }) })] }));
229
409
  };
230
410
 
231
411
  exports.ActiveWalletBalance = ActiveWalletBalance;
@@ -11,13 +11,13 @@ import { ReactComponent as SvgCheck } from '../../../../shared/assets/check.js';
11
11
  import { ReactComponent as SvgChevronDown } from '../../../../shared/assets/chevron-down.js';
12
12
  import { ReactComponent as SvgReloadIcon } from '../../../../shared/assets/reload-icon.js';
13
13
  import '../../../../context/ViewContext/ViewContext.js';
14
- import '../../../../shared/logger.js';
14
+ import { logger } from '../../../../shared/logger.js';
15
15
  import '@dynamic-labs/wallet-book';
16
16
  import '@dynamic-labs/utils';
17
17
  import '../../../../utils/constants/colors.js';
18
18
  import '../../../../utils/constants/values.js';
19
19
  import '@dynamic-labs/sdk-api-core';
20
- import '../../../../shared/consts/index.js';
20
+ import { authModalZIndex } from '../../../../shared/consts/index.js';
21
21
  import '../../../../events/dynamicEvents.js';
22
22
  import '../../../../context/CaptchaContext/CaptchaContext.js';
23
23
  import '../../../../context/ErrorContext/ErrorContext.js';
@@ -59,6 +59,7 @@ import '../../../../context/UserFieldEditorContext/UserFieldEditorContext.js';
59
59
  import '@dynamic-labs/rpc-providers';
60
60
  import '../../../../store/state/walletOptions/walletOptions.js';
61
61
  import { AccordionItem } from '../../../../components/Accordion/components/AccordionItem/AccordionItem.js';
62
+ import { Button } from '../../../../components/Button/Button.js';
62
63
  import '../../../../components/Alert/Alert.js';
63
64
  import { Typography } from '../../../../components/Typography/Typography.js';
64
65
  import '../../../../components/ShadowDOM/ShadowDOM.js';
@@ -68,13 +69,15 @@ import '../../../../components/Input/Input.js';
68
69
  import '../../../../components/IsBrowser/IsBrowser.js';
69
70
  import '../../../../components/MenuList/Dropdown/Dropdown.js';
70
71
  import '../../../../components/OverlayCard/OverlayCard.js';
72
+ import { Modal } from '../../../../components/Modal/Modal.js';
73
+ import { ModalCard } from '../../../../components/ModalCard/ModalCard.js';
71
74
  import '../../../../components/Transition/ZoomTransition/ZoomTransition.js';
72
75
  import '../../../../components/Transition/SlideInUpTransition/SlideInUpTransition.js';
73
76
  import '../../../../components/Transition/OpacityTransition/OpacityTransition.js';
74
77
  import '../../../../components/PasskeyCreatedSuccessBanner/PasskeyCreatedSuccessBanner.js';
75
78
  import '../../../../components/Popper/Popper/Popper.js';
76
79
  import '../../../../components/Popper/PopperContext/PopperContext.js';
77
- import 'react-focus-lock';
80
+ import { Portal } from '../../../../components/Portal/Portal.js';
78
81
  import 'qrcode';
79
82
  import 'formik';
80
83
  import '../../../../utils/hooks/useSubdomainCheck/useSubdomainCheck.js';
@@ -113,11 +116,12 @@ import '../../../../store/state/multichainBalances.js';
113
116
  import '@dynamic-labs/store';
114
117
  import '../../../../shared/utils/functions/getInitialUrl/getInitialUrl.js';
115
118
  import { useInternalDynamicContext } from '../../../../context/DynamicContext/useDynamicContext/useInternalDynamicContext/useInternalDynamicContext.js';
119
+ import { useAleoShieldedBalances } from '../../../../utils/hooks/useAleoShieldedBalances/useAleoShieldedBalances.js';
116
120
  import { TokenBalanceList } from './TokenBalanceList/TokenBalanceList.js';
117
121
 
118
122
  /** Component to display token balances for the primary wallet */
119
123
  const ActiveWalletBalance = ({ isLoading = false, }) => {
120
- var _a, _b, _c;
124
+ var _a, _b, _c, _d, _e;
121
125
  const { t } = useTranslation();
122
126
  /** Controls for the multi asset balance accordion */
123
127
  const [balanceIsExpanded, setBalanceIsExpanded] = useState(false);
@@ -133,11 +137,132 @@ const ActiveWalletBalance = ({ isLoading = false, }) => {
133
137
  const authMode = useAuthMode();
134
138
  const projectSettings = useProjectSettings();
135
139
  const { data: testnet } = usePromise(() => __awaiter(void 0, void 0, void 0, function* () { return Boolean(yield (primaryWallet === null || primaryWallet === void 0 ? void 0 : primaryWallet.connector.isTestnet())); }), { deps: [network], initialData: false });
136
- const { isLoading: isLoadingTokenBalances, tokenBalances, error: errorTokenBalances, fetchAccountBalances, } = useTokenBalances({
140
+ const { isLoading: isLoadingTokenBalances, tokenBalances: unshieldedTokenBalances, error: errorTokenBalances, fetchAccountBalances, } = useTokenBalances({
137
141
  chainName: primaryWallet === null || primaryWallet === void 0 ? void 0 : primaryWallet.connector.connectedChain,
138
142
  includeFiat: showFiat || !hasNativeToken,
139
143
  includeNativeBalance: true,
140
144
  });
145
+ const { tokenBalances: shieldedTokenBalances, isLoading: isLoadingShielded, refetch: refetchShielded, supportsShielded, } = useAleoShieldedBalances();
146
+ /**
147
+ * Tab state for chains that have a shielded/unshielded split. Defaults to
148
+ * shielded so Aleo wallets land on their primary balance type. Other
149
+ * chains render unchanged: when `supportsShielded` is false, the
150
+ * `tokenBalances` selector below short-circuits to unshielded
151
+ * regardless of this state, so the default has no visible effect for
152
+ * non-Aleo wallets. Only Aleo wallets see the toggle (gated by
153
+ * `supportsShielded`); they should land on Shielded since that's the
154
+ * privacy-first default for Aleo.
155
+ */
156
+ const [activeShieldTab, setActiveShieldTab] = useState('shielded');
157
+ /**
158
+ * Address of the token currently being shielded (during a click → broadcast
159
+ * cycle on the Unshielded tab's "Shield Manually" CTA). Used to flip that
160
+ * row's button to a `Working…` disabled state. Single-flight: one shield in
161
+ * flight at a time across the entire list — the form-level UX would get
162
+ * confusing otherwise (user-paid fees + multiple in-flight DPS proves).
163
+ */
164
+ const [shieldingAddress, setShieldingAddress] = useState(undefined);
165
+ // Duck-typed handle to the Aleo connector's shield helpers — only present
166
+ // on `DynamicWaasAleoConnector`. We deliberately avoid an `instanceof`
167
+ // check so this file doesn't pull a hard dependency on the Aleo package.
168
+ const aleoShieldHandle = useMemo(() => {
169
+ const c = primaryWallet === null || primaryWallet === void 0 ? void 0 : primaryWallet.connector;
170
+ if (c &&
171
+ typeof c.canShieldToken === 'function' &&
172
+ typeof c.shieldToken === 'function') {
173
+ return c;
174
+ }
175
+ return undefined;
176
+ }, [primaryWallet === null || primaryWallet === void 0 ? void 0 : primaryWallet.connector]);
177
+ // Token currently awaiting user-paid fee confirmation. Set when
178
+ // Shield Manually is clicked on a token Feemaster doesn't sponsor;
179
+ // cleared on Cancel or after the modal's Shield button dispatches.
180
+ const [pendingShieldToken, setPendingShieldToken] = useState(null);
181
+ const handleShieldToken = useCallback((token) => __awaiter(void 0, void 0, void 0, function* () {
182
+ var _f;
183
+ if (!aleoShieldHandle || shieldingAddress)
184
+ return;
185
+ // `rawBalance` is the atomic-units count redcoast surfaces (number).
186
+ // Convert to bigint via Math.round to absorb any float drift from a
187
+ // decimal-shifted display value. <=0 entries get filtered out before
188
+ // the CTA renders, but the guard here is a safety net.
189
+ const atomic = BigInt(Math.round((_f = token.rawBalance) !== null && _f !== void 0 ? _f : 0));
190
+ if (atomic <= BigInt(0))
191
+ return;
192
+ setShieldingAddress(token.address);
193
+ try {
194
+ yield aleoShieldHandle.shieldToken({
195
+ amount: atomic,
196
+ isNative: token.isNative,
197
+ tokenAddress: token.address,
198
+ });
199
+ // Refresh the unshielded list so the just-shielded balance drops to
200
+ // 0 in the redcoast feed. The shielded side will pick the new
201
+ // record up on its own polling once the RecordScanner indexes.
202
+ yield fetchAccountBalances(true);
203
+ yield refetchShielded();
204
+ }
205
+ catch (err) {
206
+ logger.debug('[ActiveWalletBalance] shieldToken failed', err);
207
+ }
208
+ finally {
209
+ setShieldingAddress(undefined);
210
+ }
211
+ }), [aleoShieldHandle, fetchAccountBalances, refetchShielded, shieldingAddress]);
212
+ const getSecondaryAction = useCallback((token) => {
213
+ // Only on the Unshielded tab, only when the connector exposes shield
214
+ // helpers, only for tokens registered as shieldable, only for tokens
215
+ // with a non-zero unshielded balance.
216
+ if (activeShieldTab !== 'unshielded')
217
+ return undefined;
218
+ if (!aleoShieldHandle)
219
+ return undefined;
220
+ if (!aleoShieldHandle.canShieldToken({
221
+ address: token.address,
222
+ isNative: token.isNative,
223
+ }))
224
+ return undefined;
225
+ if (!token.rawBalance || token.rawBalance <= 0)
226
+ return undefined;
227
+ return {
228
+ dataTestId: `shield-manually-${token.symbol}`,
229
+ isLoading: shieldingAddress === token.address,
230
+ label: 'Shield Manually',
231
+ onClick: () => {
232
+ // Pre-check Feemaster sponsorship — if covered, dispatch the
233
+ // shield silently (current behaviour). If not covered (e.g.
234
+ // policy gap, quota exhausted, lookup error), open a user-paid
235
+ // confirmation modal so the user knows they'll pay an ALEO
236
+ // network fee before we sign anything. The connector resolves
237
+ // the registry token's programId internally.
238
+ (() => __awaiter(void 0, void 0, void 0, function* () {
239
+ var _a;
240
+ const sponsored = yield ((_a = aleoShieldHandle.isShieldSponsored) === null || _a === void 0 ? void 0 : _a.call(aleoShieldHandle, {
241
+ address: token.address,
242
+ isNative: token.isNative,
243
+ }));
244
+ if (sponsored) {
245
+ handleShieldToken(token).catch(() => {
246
+ /* error surfaced via state inside handleShieldToken */
247
+ });
248
+ }
249
+ else {
250
+ setPendingShieldToken(token);
251
+ }
252
+ }))().catch(() => {
253
+ /* error surfaced via state inside the callback */
254
+ });
255
+ },
256
+ };
257
+ }, [activeShieldTab, aleoShieldHandle, handleShieldToken, shieldingAddress]);
258
+ const tokenBalances = useMemo(() => supportsShielded && activeShieldTab === 'shielded'
259
+ ? shieldedTokenBalances
260
+ : unshieldedTokenBalances, [
261
+ activeShieldTab,
262
+ shieldedTokenBalances,
263
+ supportsShielded,
264
+ unshieldedTokenBalances,
265
+ ]);
141
266
  const filteredTokenBalances = useMemo(() => (tokenBalances === null || tokenBalances === void 0 ? void 0 : tokenBalances.filter((token) => token.name)) || [], [tokenBalances]);
142
267
  const totalValue = useMemo(() => filteredTokenBalances.reduce((acc, token) => acc + ((token === null || token === void 0 ? void 0 : token.marketValue) || 0), 0), [filteredTokenBalances]);
143
268
  const enableMultiAsset = useMemo(() => {
@@ -179,7 +304,12 @@ const ActiveWalletBalance = ({ isLoading = false, }) => {
179
304
  setIsRefreshing(true);
180
305
  setIsSuccess(false);
181
306
  try {
182
- yield fetchAccountBalances(true);
307
+ if (supportsShielded && activeShieldTab === 'shielded') {
308
+ yield refetchShielded();
309
+ }
310
+ else {
311
+ yield fetchAccountBalances(true);
312
+ }
183
313
  setIsRefreshing(false);
184
314
  setIsSuccess(true);
185
315
  successTimeoutRef.current = setTimeout(() => {
@@ -193,9 +323,19 @@ const ActiveWalletBalance = ({ isLoading = false, }) => {
193
323
  refreshTimeoutRef.current = null;
194
324
  }, 1000);
195
325
  }
196
- }), [isRefreshing, isSuccess, fetchAccountBalances]);
326
+ }), [
327
+ isRefreshing,
328
+ isSuccess,
329
+ fetchAccountBalances,
330
+ refetchShielded,
331
+ supportsShielded,
332
+ activeShieldTab,
333
+ ]);
197
334
  const primaryWalletNativeBalance = () => {
198
- if (!primaryWallet || isLoadingTokenBalances || isLoading) {
335
+ if (!primaryWallet ||
336
+ isLoadingTokenBalances ||
337
+ isLoading ||
338
+ (supportsShielded && activeShieldTab === 'shielded' && isLoadingShielded)) {
199
339
  return jsx(Skeleton, { className: 'balance-container__skeleton' });
200
340
  }
201
341
  // Chain has no native token - show aggregated fiat token values
@@ -208,7 +348,11 @@ const ActiveWalletBalance = ({ isLoading = false, }) => {
208
348
  ? currencyFormatter.format(parseFloat(totalValue.toFixed(2)))
209
349
  : '<$0.01' })) : (jsx(Balance, { className: 'balance-header__balance', wallet: primaryWallet, network: network, variant: 'numbers_display' }));
210
350
  };
211
- const balanceHeaderTitle = () => (jsxs("div", { className: 'balance-header__title', children: [isLoadingTokenBalances || isLoading ? (jsx(Skeleton, { className: 'balance-header__title-skeleton' })) : (jsx(Typography, { color: 'secondary', variant: 'body_normal', copykey: 'dyn_active_wallet_info.balance', children: t('dyn_active_wallet_info.balance') })), jsxs("div", { className: 'balance-header__balance', children: [primaryWalletNativeBalance(), isSuccess ? (jsx("div", { className: 'balance-header__success-icon', children: jsx(SvgCheck, { "data-testid": 'success-icon' }) })) : (jsx(SvgReloadIcon, { "data-testid": 'refresh-balances', className: `balance-header__refresh-icon ${isRefreshing ? 'balance-header__refresh-icon--spinning' : ''}`, onClick: handleRefresh, style: {
351
+ const balanceHeaderTitle = () => (jsxs("div", { className: 'balance-header__title', children: [isLoadingTokenBalances ||
352
+ isLoading ||
353
+ (supportsShielded &&
354
+ activeShieldTab === 'shielded' &&
355
+ isLoadingShielded) ? (jsx(Skeleton, { className: 'balance-header__title-skeleton' })) : (jsx(Typography, { color: 'secondary', variant: 'body_normal', copykey: 'dyn_active_wallet_info.balance', children: t('dyn_active_wallet_info.balance') })), jsxs("div", { className: 'balance-header__balance', children: [primaryWalletNativeBalance(), isSuccess ? (jsx("div", { className: 'balance-header__success-icon', children: jsx(SvgCheck, { "data-testid": 'success-icon' }) })) : (jsx(SvgReloadIcon, { "data-testid": 'refresh-balances', className: `balance-header__refresh-icon ${isRefreshing ? 'balance-header__refresh-icon--spinning' : ''}`, onClick: handleRefresh, style: {
212
356
  cursor: isRefreshing ? 'not-allowed' : 'pointer',
213
357
  opacity: isRefreshing ? 0.5 : 1,
214
358
  }, title: 'Refresh balances' }))] })] }));
@@ -220,8 +364,44 @@ const ActiveWalletBalance = ({ isLoading = false, }) => {
220
364
  if (!showMultiAsset) {
221
365
  return jsx("div", { className: 'balance-container', children: balanceHeaderTitle() });
222
366
  }
223
- return (jsx("div", { className: 'balance-container', children: jsxs("div", { className: 'multi-asset-balance-container', children: [jsx("div", { className: `${hasShadow ? 'shadow' : ''}`, children: jsxs("button", { onClick: toggleBalanceAccordion, className: 'balance-header', "data-testid": 'balance-header', children: [balanceHeaderTitle(), !isLoadingTokenBalances && (jsx("div", { className: balanceIsExpanded ? 'balance-header__chevron' : '', children: jsx(SvgChevronDown, {}) }))] }) }), jsx(AccordionItem, { isOpen: balanceIsExpanded, className: `multi-asset-balance-container__accordion ${'multi-asset-balance-container__accordion' +
224
- (balanceIsExpanded ? '--expanded' : '--collapsed')}`, handleScroll: handleScroll, ref: contentRef, dataTestId: 'multi-asset-balance-accordion', children: jsx(TokenBalanceList, { tokenBalances: filteredTokenBalances }) })] }) }));
367
+ return (jsxs("div", { className: 'balance-container', children: [jsxs("div", { className: 'multi-asset-balance-container', children: [jsx("div", { className: `${hasShadow ? 'shadow' : ''}`, children: jsxs("button", { onClick: toggleBalanceAccordion, className: 'balance-header', "data-testid": 'balance-header', children: [balanceHeaderTitle(), !isLoadingTokenBalances && (jsx("div", { className: balanceIsExpanded ? 'balance-header__chevron' : '', children: jsx(SvgChevronDown, {}) }))] }) }), jsxs(AccordionItem, { isOpen: balanceIsExpanded, className: `multi-asset-balance-container__accordion ${'multi-asset-balance-container__accordion' +
368
+ (balanceIsExpanded ? '--expanded' : '--collapsed')}`, handleScroll: handleScroll, ref: contentRef, dataTestId: 'multi-asset-balance-accordion', children: [supportsShielded && (jsxs("div", { className: 'shield-tabs', "data-testid": 'shield-tabs', role: 'tablist', style: {
369
+ display: 'flex',
370
+ gap: 16,
371
+ padding: '12px 16px 8px 16px',
372
+ }, children: [jsx("button", { type: 'button', role: 'tab', "aria-selected": activeShieldTab === 'shielded', onClick: (e) => {
373
+ e.stopPropagation();
374
+ setActiveShieldTab('shielded');
375
+ }, style: {
376
+ background: 'none',
377
+ border: 'none',
378
+ cursor: 'pointer',
379
+ fontWeight: activeShieldTab === 'shielded' ? 600 : 400,
380
+ opacity: activeShieldTab === 'shielded' ? 1 : 0.5,
381
+ padding: 0,
382
+ }, children: jsx(Typography, { variant: 'body_normal', children: "Shielded" }) }), jsx("button", { type: 'button', role: 'tab', "aria-selected": activeShieldTab === 'unshielded', onClick: (e) => {
383
+ e.stopPropagation();
384
+ setActiveShieldTab('unshielded');
385
+ }, style: {
386
+ background: 'none',
387
+ border: 'none',
388
+ cursor: 'pointer',
389
+ fontWeight: activeShieldTab === 'unshielded' ? 600 : 400,
390
+ opacity: activeShieldTab === 'unshielded' ? 1 : 0.5,
391
+ padding: 0,
392
+ }, children: jsx(Typography, { variant: 'body_normal', children: "Unshielded" }) })] })), jsx(TokenBalanceList, { tokenBalances: filteredTokenBalances, getSecondaryAction: getSecondaryAction })] })] }), jsx(Portal, { elementId: 'dynamic-shield-confirm', isShown: Boolean(pendingShieldToken), zIndex: authModalZIndex, withBackdrop: true, handleClose: () => setPendingShieldToken(null), children: jsx(Modal, { children: jsx(ModalCard, { children: jsxs("div", { style: { padding: 24 }, children: [jsxs(Typography, { variant: 'title', weight: 'medium', color: 'primary', children: ["Shield ", (_d = pendingShieldToken === null || pendingShieldToken === void 0 ? void 0 : pendingShieldToken.balance) !== null && _d !== void 0 ? _d : '', ' ', (_e = pendingShieldToken === null || pendingShieldToken === void 0 ? void 0 : pendingShieldToken.symbol) !== null && _e !== void 0 ? _e : ''] }), jsx(Typography, { variant: 'body_normal', color: 'secondary', style: { display: 'block', marginTop: 8 }, children: "This token doesn't support auto-shielding. A network fee is required to shield manually." }), jsxs("div", { style: {
393
+ display: 'flex',
394
+ gap: 8,
395
+ marginTop: 24,
396
+ }, children: [jsx(Button, { buttonVariant: 'secondary', expanded: true, onClick: () => setPendingShieldToken(null), dataTestId: 'shield-confirm-modal-cancel', children: "Cancel" }), jsx(Button, { buttonVariant: 'primary', expanded: true, onClick: () => {
397
+ const target = pendingShieldToken;
398
+ setPendingShieldToken(null);
399
+ if (target) {
400
+ handleShieldToken(target).catch(() => {
401
+ /* error surfaced via state inside handleShieldToken */
402
+ });
403
+ }
404
+ }, dataTestId: 'shield-confirm-modal-confirm', children: "Shield" })] })] }) }) }) })] }));
225
405
  };
226
406
 
227
407
  export { ActiveWalletBalance };
@@ -117,7 +117,8 @@ const currencyFormatter = new Intl.NumberFormat('en-US', {
117
117
  currency: 'USD',
118
118
  style: 'currency',
119
119
  });
120
- const TokenBalanceItem = ({ tokenBalance, }) => {
120
+ const TokenBalanceItem = ({ tokenBalance, secondaryAction, }) => {
121
+ var _a;
121
122
  const { showFiat } = useInternalDynamicContext.useInternalDynamicContext();
122
123
  const formattedFiatValue = () => {
123
124
  var _a;
@@ -128,7 +129,9 @@ const TokenBalanceItem = ({ tokenBalance, }) => {
128
129
  ? currencyFormatter.format(parseFloat((_a = tokenBalance.marketValue) === null || _a === void 0 ? void 0 : _a.toFixed(2)))
129
130
  : '<$0.01';
130
131
  };
131
- return (jsxRuntime.jsxs("div", { className: 'token-balance-item', children: [jsxRuntime.jsxs("div", { className: 'token-balance-item__name', children: [tokenBalance.logoURI ? (jsxRuntime.jsx(Image.Image, { src: tokenBalance.logoURI, alt: tokenBalance.symbol, className: 'token-balance-item__icon', dataTestId: 'token-balance-item-icon' })) : (jsxRuntime.jsx("div", { className: 'token-balance-item__skeleton-icon', "data-testid": 'token-balance-item__skeleton-icon' })), jsxRuntime.jsx("div", { children: jsxRuntime.jsx(Typography.Typography, { variant: 'body_small', className: 'token-balance-item__title', color: 'primary', children: tokenBalance.name }) })] }), jsxRuntime.jsxs("div", { className: 'flex', children: [showFiat ? (jsxRuntime.jsx(Typography.Typography, { variant: 'body_small', weight: 'bold', color: 'primary', style: { textAlign: 'right' }, children: formattedFiatValue() })) : null, jsxRuntime.jsx("div", { className: 'token-balance-item__value', children: jsxRuntime.jsxs("div", { className: 'token-balance-item__value__balance', "data-testid": 'token-balance-item-balance', children: [jsxRuntime.jsx(Typography.Typography, { variant: 'body_small', color: 'secondary', style: { marginRight: '2px' }, children: helpers.roundBalance(String(tokenBalance.balance)) }), jsxRuntime.jsx(Typography.Typography, { variant: 'body_small', color: 'secondary', className: 'token-balance-item__symbol', children: tokenBalance.symbol })] }) })] })] }, tokenBalance.address));
132
+ return (jsxRuntime.jsxs("div", { className: 'token-balance-item', children: [jsxRuntime.jsxs("div", { className: 'token-balance-item__row', children: [jsxRuntime.jsxs("div", { className: 'token-balance-item__name', children: [tokenBalance.logoURI ? (jsxRuntime.jsx(Image.Image, { src: tokenBalance.logoURI, alt: tokenBalance.symbol, className: 'token-balance-item__icon', dataTestId: 'token-balance-item-icon' })) : (jsxRuntime.jsx("div", { className: 'token-balance-item__skeleton-icon', "data-testid": 'token-balance-item__skeleton-icon' })), jsxRuntime.jsx("div", { children: jsxRuntime.jsx(Typography.Typography, { variant: 'body_small', className: 'token-balance-item__title', color: 'primary', children: tokenBalance.name }) })] }), jsxRuntime.jsxs("div", { className: 'flex', children: [showFiat ? (jsxRuntime.jsx(Typography.Typography, { variant: 'body_small', weight: 'bold', color: 'primary', style: { textAlign: 'right' }, children: formattedFiatValue() })) : null, jsxRuntime.jsx("div", { className: 'token-balance-item__value', children: jsxRuntime.jsxs("div", { className: 'token-balance-item__value__balance', "data-testid": 'token-balance-item-balance', children: [jsxRuntime.jsx(Typography.Typography, { variant: 'body_small', color: 'secondary', style: { marginRight: '2px' }, children: helpers.roundBalance(String(tokenBalance.balance)) }), jsxRuntime.jsx(Typography.Typography, { variant: 'body_small', color: 'secondary', className: 'token-balance-item__symbol', children: tokenBalance.symbol })] }) })] })] }), secondaryAction ? (jsxRuntime.jsx("button", { type: 'button', onClick: secondaryAction.onClick, disabled: secondaryAction.isLoading, className: 'token-balance-item__secondary-action', "data-testid": (_a = secondaryAction.dataTestId) !== null && _a !== void 0 ? _a : 'token-balance-item-secondary-action', children: jsxRuntime.jsx(Typography.Typography, { variant: 'body_small', color: 'secondary', className: 'token-balance-item__secondary-action__label', children: secondaryAction.isLoading
133
+ ? 'Working…'
134
+ : `${secondaryAction.label} ›` }) })) : null] }, tokenBalance.address));
132
135
  };
133
136
 
134
137
  exports.TokenBalanceItem = TokenBalanceItem;
@@ -1,5 +1,14 @@
1
1
  /// <reference types="react" />
2
2
  import { TokenBalance } from '@dynamic-labs/sdk-api-core';
3
- export declare const TokenBalanceItem: ({ tokenBalance, }: {
3
+ export type TokenBalanceItemSecondaryAction = {
4
+ /** Right-aligned action label (e.g. "Shield Manually"). */
5
+ label: string;
6
+ onClick: () => void;
7
+ isLoading?: boolean;
8
+ /** Test id for the action button. */
9
+ dataTestId?: string;
10
+ };
11
+ export declare const TokenBalanceItem: ({ tokenBalance, secondaryAction, }: {
4
12
  tokenBalance: TokenBalance;
13
+ secondaryAction?: TokenBalanceItemSecondaryAction;
5
14
  }) => JSX.Element;