@dynamic-labs/sdk-react-core 4.80.0 → 4.82.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 (68) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/package.cjs +1 -1
  3. package/package.js +1 -1
  4. package/package.json +12 -12
  5. package/src/lib/components/SendBalanceForm/SendBalanceForm.cjs +63 -3
  6. package/src/lib/components/SendBalanceForm/SendBalanceForm.js +63 -3
  7. package/src/lib/components/SendBalanceForm/TransactionModeSegmentedControl/TransactionModeSegmentedControl.cjs +40 -0
  8. package/src/lib/components/SendBalanceForm/TransactionModeSegmentedControl/TransactionModeSegmentedControl.d.ts +16 -0
  9. package/src/lib/components/SendBalanceForm/TransactionModeSegmentedControl/TransactionModeSegmentedControl.js +36 -0
  10. package/src/lib/components/SendBalanceForm/TransactionModeSegmentedControl/icons.cjs +17 -0
  11. package/src/lib/components/SendBalanceForm/TransactionModeSegmentedControl/icons.d.ts +8 -0
  12. package/src/lib/components/SendBalanceForm/TransactionModeSegmentedControl/icons.js +12 -0
  13. package/src/lib/components/SendBalanceForm/TransactionModeSegmentedControl/index.d.ts +1 -0
  14. package/src/lib/data/api/aleo/getAleoCuratedPrices.cjs +73 -0
  15. package/src/lib/data/api/aleo/getAleoCuratedPrices.d.ts +38 -0
  16. package/src/lib/data/api/aleo/getAleoCuratedPrices.js +69 -0
  17. package/src/lib/shared/assets/index.d.ts +2 -0
  18. package/src/lib/shared/assets/midnight-shielded.cjs +54 -0
  19. package/src/lib/shared/assets/midnight-shielded.js +30 -0
  20. package/src/lib/shared/assets/midnight-unshielded.cjs +54 -0
  21. package/src/lib/shared/assets/midnight-unshielded.js +30 -0
  22. package/src/lib/styles/index.shadow.cjs +1 -1
  23. package/src/lib/styles/index.shadow.js +1 -1
  24. package/src/lib/utils/functions/compareChains/compareChains.cjs +1 -0
  25. package/src/lib/utils/functions/compareChains/compareChains.js +1 -0
  26. package/src/lib/utils/functions/getTransactionLink/blockExplorerPatterns.cjs +12 -0
  27. package/src/lib/utils/functions/getTransactionLink/blockExplorerPatterns.js +12 -0
  28. package/src/lib/utils/hooks/useAleoAutoMergeRecords/index.d.ts +1 -0
  29. package/src/lib/utils/hooks/useAleoAutoMergeRecords/useAleoAutoMergeRecords.cjs +246 -0
  30. package/src/lib/utils/hooks/useAleoAutoMergeRecords/useAleoAutoMergeRecords.d.ts +17 -0
  31. package/src/lib/utils/hooks/useAleoAutoMergeRecords/useAleoAutoMergeRecords.js +242 -0
  32. package/src/lib/utils/hooks/useAleoAutoShieldSponsoredTokens/index.d.ts +1 -0
  33. package/src/lib/utils/hooks/useAleoAutoShieldSponsoredTokens/useAleoAutoShieldSponsoredTokens.cjs +263 -0
  34. package/src/lib/utils/hooks/useAleoAutoShieldSponsoredTokens/useAleoAutoShieldSponsoredTokens.d.ts +59 -0
  35. package/src/lib/utils/hooks/useAleoAutoShieldSponsoredTokens/useAleoAutoShieldSponsoredTokens.js +259 -0
  36. package/src/lib/utils/hooks/useAleoShieldedBalances/index.d.ts +1 -0
  37. package/src/lib/utils/hooks/useAleoShieldedBalances/useAleoShieldedBalances.cjs +443 -0
  38. package/src/lib/utils/hooks/useAleoShieldedBalances/useAleoShieldedBalances.d.ts +24 -0
  39. package/src/lib/utils/hooks/useAleoShieldedBalances/useAleoShieldedBalances.js +439 -0
  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/views/BackupUnsuccessfulView/BackupUnsuccessfulView.cjs +12 -1
  44. package/src/lib/views/BackupUnsuccessfulView/BackupUnsuccessfulView.js +12 -1
  45. package/src/lib/views/SendBalanceView/SendBalanceView.cjs +53 -0
  46. package/src/lib/views/SendBalanceView/SendBalanceView.js +53 -0
  47. package/src/lib/widgets/DynamicWidget/components/ActiveMidnightWalletBalance/ActiveMidnightWalletBalance.cjs +193 -0
  48. package/src/lib/widgets/DynamicWidget/components/ActiveMidnightWalletBalance/ActiveMidnightWalletBalance.d.ts +7 -0
  49. package/src/lib/widgets/DynamicWidget/components/ActiveMidnightWalletBalance/ActiveMidnightWalletBalance.js +189 -0
  50. package/src/lib/widgets/DynamicWidget/components/ActiveMidnightWalletBalance/index.d.ts +1 -0
  51. package/src/lib/widgets/DynamicWidget/components/ActiveWalletBalance/ActiveWalletBalance.cjs +216 -11
  52. package/src/lib/widgets/DynamicWidget/components/ActiveWalletBalance/ActiveWalletBalance.js +216 -11
  53. package/src/lib/widgets/DynamicWidget/components/ActiveWalletBalance/TokenBalanceItem/TokenBalanceItem.cjs +5 -2
  54. package/src/lib/widgets/DynamicWidget/components/ActiveWalletBalance/TokenBalanceItem/TokenBalanceItem.d.ts +10 -1
  55. package/src/lib/widgets/DynamicWidget/components/ActiveWalletBalance/TokenBalanceItem/TokenBalanceItem.js +5 -2
  56. package/src/lib/widgets/DynamicWidget/components/ActiveWalletBalance/TokenBalanceItem/index.d.ts +1 -0
  57. package/src/lib/widgets/DynamicWidget/components/ActiveWalletBalance/TokenBalanceList/TokenBalanceList.cjs +2 -2
  58. package/src/lib/widgets/DynamicWidget/components/ActiveWalletBalance/TokenBalanceList/TokenBalanceList.d.ts +3 -1
  59. package/src/lib/widgets/DynamicWidget/components/ActiveWalletBalance/TokenBalanceList/TokenBalanceList.js +2 -2
  60. package/src/lib/widgets/DynamicWidget/components/ActiveWalletInformation/ActiveMidnightWalletAddresses/ActiveMidnightWalletAddresses.cjs +124 -0
  61. package/src/lib/widgets/DynamicWidget/components/ActiveWalletInformation/ActiveMidnightWalletAddresses/ActiveMidnightWalletAddresses.d.ts +9 -0
  62. package/src/lib/widgets/DynamicWidget/components/ActiveWalletInformation/ActiveMidnightWalletAddresses/ActiveMidnightWalletAddresses.js +120 -0
  63. package/src/lib/widgets/DynamicWidget/components/ActiveWalletInformation/ActiveMidnightWalletAddresses/index.d.ts +1 -0
  64. package/src/lib/widgets/DynamicWidget/components/ActiveWalletInformation/ActiveWalletInformation.cjs +21 -10
  65. package/src/lib/widgets/DynamicWidget/components/ActiveWalletInformation/ActiveWalletInformation.js +22 -11
  66. package/src/lib/widgets/DynamicWidget/components/WalletDetailsCard/WalletDetailsCard.cjs +22 -2
  67. package/src/lib/widgets/DynamicWidget/components/WalletDetailsCard/WalletDetailsCard.d.ts +8 -1
  68. package/src/lib/widgets/DynamicWidget/components/WalletDetailsCard/WalletDetailsCard.js +23 -3
@@ -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,14 @@ 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');
124
+ var useAleoAutoMergeRecords = require('../../../../utils/hooks/useAleoAutoMergeRecords/useAleoAutoMergeRecords.cjs');
125
+ var useAleoAutoShieldSponsoredTokens = require('../../../../utils/hooks/useAleoAutoShieldSponsoredTokens/useAleoAutoShieldSponsoredTokens.cjs');
120
126
  var TokenBalanceList = require('./TokenBalanceList/TokenBalanceList.cjs');
121
127
 
122
128
  /** Component to display token balances for the primary wallet */
123
129
  const ActiveWalletBalance = ({ isLoading = false, }) => {
124
- var _a, _b, _c;
130
+ var _a, _b, _c, _d, _e;
125
131
  const { t } = reactI18next.useTranslation();
126
132
  /** Controls for the multi asset balance accordion */
127
133
  const [balanceIsExpanded, setBalanceIsExpanded] = React.useState(false);
@@ -137,11 +143,152 @@ const ActiveWalletBalance = ({ isLoading = false, }) => {
137
143
  const authMode$1 = authMode.useAuthMode();
138
144
  const projectSettings = useProjectSettings.useProjectSettings();
139
145
  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({
146
+ const { isLoading: isLoadingTokenBalances, tokenBalances: unshieldedTokenBalances, error: errorTokenBalances, fetchAccountBalances, } = useTokenBalances.useTokenBalances({
141
147
  chainName: primaryWallet === null || primaryWallet === void 0 ? void 0 : primaryWallet.connector.connectedChain,
142
148
  includeFiat: showFiat || !hasNativeToken,
143
149
  includeNativeBalance: true,
144
150
  });
151
+ const { tokenBalances: shieldedTokenBalances, isLoading: isLoadingShielded, refetch: refetchShielded, supportsShielded, } = useAleoShieldedBalances.useAleoShieldedBalances();
152
+ /**
153
+ * Tab state for chains that have a shielded/unshielded split. Defaults to
154
+ * shielded so Aleo wallets land on their primary balance type. Other
155
+ * chains render unchanged: when `supportsShielded` is false, the
156
+ * `tokenBalances` selector below short-circuits to unshielded
157
+ * regardless of this state, so the default has no visible effect for
158
+ * non-Aleo wallets. Only Aleo wallets see the toggle (gated by
159
+ * `supportsShielded`); they should land on Shielded since that's the
160
+ * privacy-first default for Aleo.
161
+ */
162
+ const [activeShieldTab, setActiveShieldTab] = React.useState('shielded');
163
+ /**
164
+ * Address of the token currently being shielded (during a click → broadcast
165
+ * cycle on the Unshielded tab's "Shield Manually" CTA). Used to flip that
166
+ * row's button to a `Working…` disabled state. Single-flight: one shield in
167
+ * flight at a time across the entire list — the form-level UX would get
168
+ * confusing otherwise (user-paid fees + multiple in-flight DPS proves).
169
+ */
170
+ const [shieldingAddress, setShieldingAddress] = React.useState(undefined);
171
+ // Duck-typed handle to the Aleo connector's shield helpers — only present
172
+ // on `DynamicWaasAleoConnector`. We deliberately avoid an `instanceof`
173
+ // check so this file doesn't pull a hard dependency on the Aleo package.
174
+ const aleoShieldHandle = React.useMemo(() => {
175
+ const c = primaryWallet === null || primaryWallet === void 0 ? void 0 : primaryWallet.connector;
176
+ if (c &&
177
+ typeof c.canShieldToken === 'function' &&
178
+ typeof c.shieldToken === 'function') {
179
+ return c;
180
+ }
181
+ return undefined;
182
+ }, [primaryWallet === null || primaryWallet === void 0 ? void 0 : primaryWallet.connector]);
183
+ // Auto-merge sponsored programs on wallet load. Reads owned records,
184
+ // groups by program, and silently fires `joinRecords` per program with
185
+ // ≥2 records when the Aleo Feemaster covers `join`. Programs that
186
+ // Feemaster doesn't sponsor are skipped — the manual "Merge all
187
+ // records" demo CTA still dispatches user-paid for those.
188
+ useAleoAutoMergeRecords.useAleoAutoMergeRecords();
189
+ // Auto-shield sponsored tokens on wallet load (and on each unshielded
190
+ // balance refresh). Iterates the unshielded list and silently fires
191
+ // `shieldToken` for tokens whose `transfer_public_to_private` is
192
+ // Feemaster-sponsored. Unsponsored tokens stay on the manual Shield
193
+ // Manually CTA, which prompts the user-paid confirmation modal.
194
+ const { isShielding: isAutoShielding } = useAleoAutoShieldSponsoredTokens.useAleoAutoShieldSponsoredTokens({
195
+ accountAddress: primaryWallet === null || primaryWallet === void 0 ? void 0 : primaryWallet.address,
196
+ onShielded: React.useCallback(() => _tslib.__awaiter(void 0, void 0, void 0, function* () {
197
+ yield fetchAccountBalances(true);
198
+ yield refetchShielded();
199
+ }), [fetchAccountBalances, refetchShielded]),
200
+ shieldHandle: aleoShieldHandle,
201
+ unshieldedTokenBalances: unshieldedTokenBalances,
202
+ });
203
+ // Token currently awaiting user-paid fee confirmation. Set when
204
+ // Shield Manually is clicked on a token Feemaster doesn't sponsor;
205
+ // cleared on Cancel or after the modal's Shield button dispatches.
206
+ const [pendingShieldToken, setPendingShieldToken] = React.useState(null);
207
+ const handleShieldToken = React.useCallback((token) => _tslib.__awaiter(void 0, void 0, void 0, function* () {
208
+ var _f;
209
+ if (!aleoShieldHandle || shieldingAddress)
210
+ return;
211
+ // `rawBalance` is the atomic-units count redcoast surfaces (number).
212
+ // Convert to bigint via Math.round to absorb any float drift from a
213
+ // decimal-shifted display value. <=0 entries get filtered out before
214
+ // the CTA renders, but the guard here is a safety net.
215
+ const atomic = BigInt(Math.round((_f = token.rawBalance) !== null && _f !== void 0 ? _f : 0));
216
+ if (atomic <= BigInt(0))
217
+ return;
218
+ setShieldingAddress(token.address);
219
+ try {
220
+ yield aleoShieldHandle.shieldToken({
221
+ amount: atomic,
222
+ isNative: token.isNative,
223
+ tokenAddress: token.address,
224
+ });
225
+ // Refresh the unshielded list so the just-shielded balance drops to
226
+ // 0 in the redcoast feed. The shielded side will pick the new
227
+ // record up on its own polling once the RecordScanner indexes.
228
+ yield fetchAccountBalances(true);
229
+ yield refetchShielded();
230
+ }
231
+ catch (err) {
232
+ logger.logger.debug('[ActiveWalletBalance] shieldToken failed', err);
233
+ }
234
+ finally {
235
+ setShieldingAddress(undefined);
236
+ }
237
+ }), [aleoShieldHandle, fetchAccountBalances, refetchShielded, shieldingAddress]);
238
+ const getSecondaryAction = React.useCallback((token) => {
239
+ // Only on the Unshielded tab, only when the connector exposes shield
240
+ // helpers, only for tokens registered as shieldable, only for tokens
241
+ // with a non-zero unshielded balance.
242
+ if (activeShieldTab !== 'unshielded')
243
+ return undefined;
244
+ if (!aleoShieldHandle)
245
+ return undefined;
246
+ if (!aleoShieldHandle.canShieldToken({
247
+ address: token.address,
248
+ isNative: token.isNative,
249
+ }))
250
+ return undefined;
251
+ if (!token.rawBalance || token.rawBalance <= 0)
252
+ return undefined;
253
+ return {
254
+ dataTestId: `shield-manually-${token.symbol}`,
255
+ isLoading: shieldingAddress === token.address,
256
+ label: 'Shield Manually',
257
+ onClick: () => {
258
+ // Pre-check Feemaster sponsorship — if covered, dispatch the
259
+ // shield silently (current behaviour). If not covered (e.g.
260
+ // policy gap, quota exhausted, lookup error), open a user-paid
261
+ // confirmation modal so the user knows they'll pay an ALEO
262
+ // network fee before we sign anything. The connector resolves
263
+ // the registry token's programId internally.
264
+ (() => _tslib.__awaiter(void 0, void 0, void 0, function* () {
265
+ var _a;
266
+ const sponsored = yield ((_a = aleoShieldHandle.isShieldSponsored) === null || _a === void 0 ? void 0 : _a.call(aleoShieldHandle, {
267
+ address: token.address,
268
+ isNative: token.isNative,
269
+ }));
270
+ if (sponsored) {
271
+ handleShieldToken(token).catch(() => {
272
+ /* error surfaced via state inside handleShieldToken */
273
+ });
274
+ }
275
+ else {
276
+ setPendingShieldToken(token);
277
+ }
278
+ }))().catch(() => {
279
+ /* error surfaced via state inside the callback */
280
+ });
281
+ },
282
+ };
283
+ }, [activeShieldTab, aleoShieldHandle, handleShieldToken, shieldingAddress]);
284
+ const tokenBalances = React.useMemo(() => supportsShielded && activeShieldTab === 'shielded'
285
+ ? shieldedTokenBalances
286
+ : unshieldedTokenBalances, [
287
+ activeShieldTab,
288
+ shieldedTokenBalances,
289
+ supportsShielded,
290
+ unshieldedTokenBalances,
291
+ ]);
145
292
  const filteredTokenBalances = React.useMemo(() => (tokenBalances === null || tokenBalances === void 0 ? void 0 : tokenBalances.filter((token) => token.name)) || [], [tokenBalances]);
146
293
  const totalValue = React.useMemo(() => filteredTokenBalances.reduce((acc, token) => acc + ((token === null || token === void 0 ? void 0 : token.marketValue) || 0), 0), [filteredTokenBalances]);
147
294
  const enableMultiAsset = React.useMemo(() => {
@@ -183,7 +330,12 @@ const ActiveWalletBalance = ({ isLoading = false, }) => {
183
330
  setIsRefreshing(true);
184
331
  setIsSuccess(false);
185
332
  try {
186
- yield fetchAccountBalances(true);
333
+ if (supportsShielded && activeShieldTab === 'shielded') {
334
+ yield refetchShielded();
335
+ }
336
+ else {
337
+ yield fetchAccountBalances(true);
338
+ }
187
339
  setIsRefreshing(false);
188
340
  setIsSuccess(true);
189
341
  successTimeoutRef.current = setTimeout(() => {
@@ -197,9 +349,19 @@ const ActiveWalletBalance = ({ isLoading = false, }) => {
197
349
  refreshTimeoutRef.current = null;
198
350
  }, 1000);
199
351
  }
200
- }), [isRefreshing, isSuccess, fetchAccountBalances]);
352
+ }), [
353
+ isRefreshing,
354
+ isSuccess,
355
+ fetchAccountBalances,
356
+ refetchShielded,
357
+ supportsShielded,
358
+ activeShieldTab,
359
+ ]);
201
360
  const primaryWalletNativeBalance = () => {
202
- if (!primaryWallet || isLoadingTokenBalances || isLoading) {
361
+ if (!primaryWallet ||
362
+ isLoadingTokenBalances ||
363
+ isLoading ||
364
+ (supportsShielded && activeShieldTab === 'shielded' && isLoadingShielded)) {
203
365
  return jsxRuntime.jsx(Skeleton.Skeleton, { className: 'balance-container__skeleton' });
204
366
  }
205
367
  // Chain has no native token - show aggregated fiat token values
@@ -212,7 +374,11 @@ const ActiveWalletBalance = ({ isLoading = false, }) => {
212
374
  ? currencyFormatter.format(parseFloat(totalValue.toFixed(2)))
213
375
  : '<$0.01' })) : (jsxRuntime.jsx(Balance.Balance, { className: 'balance-header__balance', wallet: primaryWallet, network: network, variant: 'numbers_display' }));
214
376
  };
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: {
377
+ const balanceHeaderTitle = () => (jsxRuntime.jsxs("div", { className: 'balance-header__title', children: [isLoadingTokenBalances ||
378
+ isLoading ||
379
+ (supportsShielded &&
380
+ activeShieldTab === 'shielded' &&
381
+ 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
382
  cursor: isRefreshing ? 'not-allowed' : 'pointer',
217
383
  opacity: isRefreshing ? 0.5 : 1,
218
384
  }, title: 'Refresh balances' }))] })] }));
@@ -224,8 +390,47 @@ const ActiveWalletBalance = ({ isLoading = false, }) => {
224
390
  if (!showMultiAsset) {
225
391
  return jsxRuntime.jsx("div", { className: 'balance-container', children: balanceHeaderTitle() });
226
392
  }
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 }) })] }) }));
393
+ 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' +
394
+ (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: {
395
+ display: 'flex',
396
+ gap: 16,
397
+ padding: '12px 16px 8px 16px',
398
+ }, children: [jsxRuntime.jsx("button", { type: 'button', role: 'tab', "aria-selected": activeShieldTab === 'shielded', onClick: (e) => {
399
+ e.stopPropagation();
400
+ setActiveShieldTab('shielded');
401
+ }, style: {
402
+ background: 'none',
403
+ border: 'none',
404
+ cursor: 'pointer',
405
+ fontWeight: activeShieldTab === 'shielded' ? 600 : 400,
406
+ opacity: activeShieldTab === 'shielded' ? 1 : 0.5,
407
+ padding: 0,
408
+ }, children: jsxRuntime.jsx(Typography.Typography, { variant: 'body_normal', children: "Shielded" }) }), jsxRuntime.jsx("button", { type: 'button', role: 'tab', "aria-selected": activeShieldTab === 'unshielded', onClick: (e) => {
409
+ e.stopPropagation();
410
+ setActiveShieldTab('unshielded');
411
+ }, style: {
412
+ background: 'none',
413
+ border: 'none',
414
+ cursor: 'pointer',
415
+ fontWeight: activeShieldTab === 'unshielded' ? 600 : 400,
416
+ opacity: activeShieldTab === 'unshielded' ? 1 : 0.5,
417
+ padding: 0,
418
+ }, children: jsxRuntime.jsx(Typography.Typography, { variant: 'body_normal', children: "Unshielded" }) })] })), isAutoShielding && (jsxRuntime.jsx("div", { className: 'auto-shield-status', "data-testid": 'auto-shield-status', style: {
419
+ opacity: 0.7,
420
+ padding: '4px 16px 8px 16px',
421
+ }, children: jsxRuntime.jsx(Typography.Typography, { variant: 'body_small', children: "Auto-shielding\u2026" }) })), 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: {
422
+ display: 'flex',
423
+ gap: 8,
424
+ marginTop: 24,
425
+ }, 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: () => {
426
+ const target = pendingShieldToken;
427
+ setPendingShieldToken(null);
428
+ if (target) {
429
+ handleShieldToken(target).catch(() => {
430
+ /* error surfaced via state inside handleShieldToken */
431
+ });
432
+ }
433
+ }, dataTestId: 'shield-confirm-modal-confirm', children: "Shield" })] })] }) }) }) })] }));
229
434
  };
230
435
 
231
436
  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,14 @@ 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';
120
+ import { useAleoAutoMergeRecords } from '../../../../utils/hooks/useAleoAutoMergeRecords/useAleoAutoMergeRecords.js';
121
+ import { useAleoAutoShieldSponsoredTokens } from '../../../../utils/hooks/useAleoAutoShieldSponsoredTokens/useAleoAutoShieldSponsoredTokens.js';
116
122
  import { TokenBalanceList } from './TokenBalanceList/TokenBalanceList.js';
117
123
 
118
124
  /** Component to display token balances for the primary wallet */
119
125
  const ActiveWalletBalance = ({ isLoading = false, }) => {
120
- var _a, _b, _c;
126
+ var _a, _b, _c, _d, _e;
121
127
  const { t } = useTranslation();
122
128
  /** Controls for the multi asset balance accordion */
123
129
  const [balanceIsExpanded, setBalanceIsExpanded] = useState(false);
@@ -133,11 +139,152 @@ const ActiveWalletBalance = ({ isLoading = false, }) => {
133
139
  const authMode = useAuthMode();
134
140
  const projectSettings = useProjectSettings();
135
141
  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({
142
+ const { isLoading: isLoadingTokenBalances, tokenBalances: unshieldedTokenBalances, error: errorTokenBalances, fetchAccountBalances, } = useTokenBalances({
137
143
  chainName: primaryWallet === null || primaryWallet === void 0 ? void 0 : primaryWallet.connector.connectedChain,
138
144
  includeFiat: showFiat || !hasNativeToken,
139
145
  includeNativeBalance: true,
140
146
  });
147
+ const { tokenBalances: shieldedTokenBalances, isLoading: isLoadingShielded, refetch: refetchShielded, supportsShielded, } = useAleoShieldedBalances();
148
+ /**
149
+ * Tab state for chains that have a shielded/unshielded split. Defaults to
150
+ * shielded so Aleo wallets land on their primary balance type. Other
151
+ * chains render unchanged: when `supportsShielded` is false, the
152
+ * `tokenBalances` selector below short-circuits to unshielded
153
+ * regardless of this state, so the default has no visible effect for
154
+ * non-Aleo wallets. Only Aleo wallets see the toggle (gated by
155
+ * `supportsShielded`); they should land on Shielded since that's the
156
+ * privacy-first default for Aleo.
157
+ */
158
+ const [activeShieldTab, setActiveShieldTab] = useState('shielded');
159
+ /**
160
+ * Address of the token currently being shielded (during a click → broadcast
161
+ * cycle on the Unshielded tab's "Shield Manually" CTA). Used to flip that
162
+ * row's button to a `Working…` disabled state. Single-flight: one shield in
163
+ * flight at a time across the entire list — the form-level UX would get
164
+ * confusing otherwise (user-paid fees + multiple in-flight DPS proves).
165
+ */
166
+ const [shieldingAddress, setShieldingAddress] = useState(undefined);
167
+ // Duck-typed handle to the Aleo connector's shield helpers — only present
168
+ // on `DynamicWaasAleoConnector`. We deliberately avoid an `instanceof`
169
+ // check so this file doesn't pull a hard dependency on the Aleo package.
170
+ const aleoShieldHandle = useMemo(() => {
171
+ const c = primaryWallet === null || primaryWallet === void 0 ? void 0 : primaryWallet.connector;
172
+ if (c &&
173
+ typeof c.canShieldToken === 'function' &&
174
+ typeof c.shieldToken === 'function') {
175
+ return c;
176
+ }
177
+ return undefined;
178
+ }, [primaryWallet === null || primaryWallet === void 0 ? void 0 : primaryWallet.connector]);
179
+ // Auto-merge sponsored programs on wallet load. Reads owned records,
180
+ // groups by program, and silently fires `joinRecords` per program with
181
+ // ≥2 records when the Aleo Feemaster covers `join`. Programs that
182
+ // Feemaster doesn't sponsor are skipped — the manual "Merge all
183
+ // records" demo CTA still dispatches user-paid for those.
184
+ useAleoAutoMergeRecords();
185
+ // Auto-shield sponsored tokens on wallet load (and on each unshielded
186
+ // balance refresh). Iterates the unshielded list and silently fires
187
+ // `shieldToken` for tokens whose `transfer_public_to_private` is
188
+ // Feemaster-sponsored. Unsponsored tokens stay on the manual Shield
189
+ // Manually CTA, which prompts the user-paid confirmation modal.
190
+ const { isShielding: isAutoShielding } = useAleoAutoShieldSponsoredTokens({
191
+ accountAddress: primaryWallet === null || primaryWallet === void 0 ? void 0 : primaryWallet.address,
192
+ onShielded: useCallback(() => __awaiter(void 0, void 0, void 0, function* () {
193
+ yield fetchAccountBalances(true);
194
+ yield refetchShielded();
195
+ }), [fetchAccountBalances, refetchShielded]),
196
+ shieldHandle: aleoShieldHandle,
197
+ unshieldedTokenBalances: unshieldedTokenBalances,
198
+ });
199
+ // Token currently awaiting user-paid fee confirmation. Set when
200
+ // Shield Manually is clicked on a token Feemaster doesn't sponsor;
201
+ // cleared on Cancel or after the modal's Shield button dispatches.
202
+ const [pendingShieldToken, setPendingShieldToken] = useState(null);
203
+ const handleShieldToken = useCallback((token) => __awaiter(void 0, void 0, void 0, function* () {
204
+ var _f;
205
+ if (!aleoShieldHandle || shieldingAddress)
206
+ return;
207
+ // `rawBalance` is the atomic-units count redcoast surfaces (number).
208
+ // Convert to bigint via Math.round to absorb any float drift from a
209
+ // decimal-shifted display value. <=0 entries get filtered out before
210
+ // the CTA renders, but the guard here is a safety net.
211
+ const atomic = BigInt(Math.round((_f = token.rawBalance) !== null && _f !== void 0 ? _f : 0));
212
+ if (atomic <= BigInt(0))
213
+ return;
214
+ setShieldingAddress(token.address);
215
+ try {
216
+ yield aleoShieldHandle.shieldToken({
217
+ amount: atomic,
218
+ isNative: token.isNative,
219
+ tokenAddress: token.address,
220
+ });
221
+ // Refresh the unshielded list so the just-shielded balance drops to
222
+ // 0 in the redcoast feed. The shielded side will pick the new
223
+ // record up on its own polling once the RecordScanner indexes.
224
+ yield fetchAccountBalances(true);
225
+ yield refetchShielded();
226
+ }
227
+ catch (err) {
228
+ logger.debug('[ActiveWalletBalance] shieldToken failed', err);
229
+ }
230
+ finally {
231
+ setShieldingAddress(undefined);
232
+ }
233
+ }), [aleoShieldHandle, fetchAccountBalances, refetchShielded, shieldingAddress]);
234
+ const getSecondaryAction = useCallback((token) => {
235
+ // Only on the Unshielded tab, only when the connector exposes shield
236
+ // helpers, only for tokens registered as shieldable, only for tokens
237
+ // with a non-zero unshielded balance.
238
+ if (activeShieldTab !== 'unshielded')
239
+ return undefined;
240
+ if (!aleoShieldHandle)
241
+ return undefined;
242
+ if (!aleoShieldHandle.canShieldToken({
243
+ address: token.address,
244
+ isNative: token.isNative,
245
+ }))
246
+ return undefined;
247
+ if (!token.rawBalance || token.rawBalance <= 0)
248
+ return undefined;
249
+ return {
250
+ dataTestId: `shield-manually-${token.symbol}`,
251
+ isLoading: shieldingAddress === token.address,
252
+ label: 'Shield Manually',
253
+ onClick: () => {
254
+ // Pre-check Feemaster sponsorship — if covered, dispatch the
255
+ // shield silently (current behaviour). If not covered (e.g.
256
+ // policy gap, quota exhausted, lookup error), open a user-paid
257
+ // confirmation modal so the user knows they'll pay an ALEO
258
+ // network fee before we sign anything. The connector resolves
259
+ // the registry token's programId internally.
260
+ (() => __awaiter(void 0, void 0, void 0, function* () {
261
+ var _a;
262
+ const sponsored = yield ((_a = aleoShieldHandle.isShieldSponsored) === null || _a === void 0 ? void 0 : _a.call(aleoShieldHandle, {
263
+ address: token.address,
264
+ isNative: token.isNative,
265
+ }));
266
+ if (sponsored) {
267
+ handleShieldToken(token).catch(() => {
268
+ /* error surfaced via state inside handleShieldToken */
269
+ });
270
+ }
271
+ else {
272
+ setPendingShieldToken(token);
273
+ }
274
+ }))().catch(() => {
275
+ /* error surfaced via state inside the callback */
276
+ });
277
+ },
278
+ };
279
+ }, [activeShieldTab, aleoShieldHandle, handleShieldToken, shieldingAddress]);
280
+ const tokenBalances = useMemo(() => supportsShielded && activeShieldTab === 'shielded'
281
+ ? shieldedTokenBalances
282
+ : unshieldedTokenBalances, [
283
+ activeShieldTab,
284
+ shieldedTokenBalances,
285
+ supportsShielded,
286
+ unshieldedTokenBalances,
287
+ ]);
141
288
  const filteredTokenBalances = useMemo(() => (tokenBalances === null || tokenBalances === void 0 ? void 0 : tokenBalances.filter((token) => token.name)) || [], [tokenBalances]);
142
289
  const totalValue = useMemo(() => filteredTokenBalances.reduce((acc, token) => acc + ((token === null || token === void 0 ? void 0 : token.marketValue) || 0), 0), [filteredTokenBalances]);
143
290
  const enableMultiAsset = useMemo(() => {
@@ -179,7 +326,12 @@ const ActiveWalletBalance = ({ isLoading = false, }) => {
179
326
  setIsRefreshing(true);
180
327
  setIsSuccess(false);
181
328
  try {
182
- yield fetchAccountBalances(true);
329
+ if (supportsShielded && activeShieldTab === 'shielded') {
330
+ yield refetchShielded();
331
+ }
332
+ else {
333
+ yield fetchAccountBalances(true);
334
+ }
183
335
  setIsRefreshing(false);
184
336
  setIsSuccess(true);
185
337
  successTimeoutRef.current = setTimeout(() => {
@@ -193,9 +345,19 @@ const ActiveWalletBalance = ({ isLoading = false, }) => {
193
345
  refreshTimeoutRef.current = null;
194
346
  }, 1000);
195
347
  }
196
- }), [isRefreshing, isSuccess, fetchAccountBalances]);
348
+ }), [
349
+ isRefreshing,
350
+ isSuccess,
351
+ fetchAccountBalances,
352
+ refetchShielded,
353
+ supportsShielded,
354
+ activeShieldTab,
355
+ ]);
197
356
  const primaryWalletNativeBalance = () => {
198
- if (!primaryWallet || isLoadingTokenBalances || isLoading) {
357
+ if (!primaryWallet ||
358
+ isLoadingTokenBalances ||
359
+ isLoading ||
360
+ (supportsShielded && activeShieldTab === 'shielded' && isLoadingShielded)) {
199
361
  return jsx(Skeleton, { className: 'balance-container__skeleton' });
200
362
  }
201
363
  // Chain has no native token - show aggregated fiat token values
@@ -208,7 +370,11 @@ const ActiveWalletBalance = ({ isLoading = false, }) => {
208
370
  ? currencyFormatter.format(parseFloat(totalValue.toFixed(2)))
209
371
  : '<$0.01' })) : (jsx(Balance, { className: 'balance-header__balance', wallet: primaryWallet, network: network, variant: 'numbers_display' }));
210
372
  };
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: {
373
+ const balanceHeaderTitle = () => (jsxs("div", { className: 'balance-header__title', children: [isLoadingTokenBalances ||
374
+ isLoading ||
375
+ (supportsShielded &&
376
+ activeShieldTab === 'shielded' &&
377
+ 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
378
  cursor: isRefreshing ? 'not-allowed' : 'pointer',
213
379
  opacity: isRefreshing ? 0.5 : 1,
214
380
  }, title: 'Refresh balances' }))] })] }));
@@ -220,8 +386,47 @@ const ActiveWalletBalance = ({ isLoading = false, }) => {
220
386
  if (!showMultiAsset) {
221
387
  return jsx("div", { className: 'balance-container', children: balanceHeaderTitle() });
222
388
  }
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 }) })] }) }));
389
+ 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' +
390
+ (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: {
391
+ display: 'flex',
392
+ gap: 16,
393
+ padding: '12px 16px 8px 16px',
394
+ }, children: [jsx("button", { type: 'button', role: 'tab', "aria-selected": activeShieldTab === 'shielded', onClick: (e) => {
395
+ e.stopPropagation();
396
+ setActiveShieldTab('shielded');
397
+ }, style: {
398
+ background: 'none',
399
+ border: 'none',
400
+ cursor: 'pointer',
401
+ fontWeight: activeShieldTab === 'shielded' ? 600 : 400,
402
+ opacity: activeShieldTab === 'shielded' ? 1 : 0.5,
403
+ padding: 0,
404
+ }, children: jsx(Typography, { variant: 'body_normal', children: "Shielded" }) }), jsx("button", { type: 'button', role: 'tab', "aria-selected": activeShieldTab === 'unshielded', onClick: (e) => {
405
+ e.stopPropagation();
406
+ setActiveShieldTab('unshielded');
407
+ }, style: {
408
+ background: 'none',
409
+ border: 'none',
410
+ cursor: 'pointer',
411
+ fontWeight: activeShieldTab === 'unshielded' ? 600 : 400,
412
+ opacity: activeShieldTab === 'unshielded' ? 1 : 0.5,
413
+ padding: 0,
414
+ }, children: jsx(Typography, { variant: 'body_normal', children: "Unshielded" }) })] })), isAutoShielding && (jsx("div", { className: 'auto-shield-status', "data-testid": 'auto-shield-status', style: {
415
+ opacity: 0.7,
416
+ padding: '4px 16px 8px 16px',
417
+ }, children: jsx(Typography, { variant: 'body_small', children: "Auto-shielding\u2026" }) })), 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: {
418
+ display: 'flex',
419
+ gap: 8,
420
+ marginTop: 24,
421
+ }, children: [jsx(Button, { buttonVariant: 'secondary', expanded: true, onClick: () => setPendingShieldToken(null), dataTestId: 'shield-confirm-modal-cancel', children: "Cancel" }), jsx(Button, { buttonVariant: 'primary', expanded: true, onClick: () => {
422
+ const target = pendingShieldToken;
423
+ setPendingShieldToken(null);
424
+ if (target) {
425
+ handleShieldToken(target).catch(() => {
426
+ /* error surfaced via state inside handleShieldToken */
427
+ });
428
+ }
429
+ }, dataTestId: 'shield-confirm-modal-confirm', children: "Shield" })] })] }) }) }) })] }));
225
430
  };
226
431
 
227
432
  export { ActiveWalletBalance };