@dynamic-labs/sdk-react-core 4.80.0 → 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.
- package/CHANGELOG.md +16 -0
- package/package.cjs +1 -1
- package/package.js +1 -1
- package/package.json +12 -12
- package/src/lib/components/SendBalanceForm/SendBalanceForm.cjs +63 -3
- package/src/lib/components/SendBalanceForm/SendBalanceForm.js +63 -3
- package/src/lib/components/SendBalanceForm/TransactionModeSegmentedControl/TransactionModeSegmentedControl.cjs +40 -0
- package/src/lib/components/SendBalanceForm/TransactionModeSegmentedControl/TransactionModeSegmentedControl.d.ts +16 -0
- package/src/lib/components/SendBalanceForm/TransactionModeSegmentedControl/TransactionModeSegmentedControl.js +36 -0
- package/src/lib/components/SendBalanceForm/TransactionModeSegmentedControl/icons.cjs +17 -0
- package/src/lib/components/SendBalanceForm/TransactionModeSegmentedControl/icons.d.ts +8 -0
- package/src/lib/components/SendBalanceForm/TransactionModeSegmentedControl/icons.js +12 -0
- package/src/lib/components/SendBalanceForm/TransactionModeSegmentedControl/index.d.ts +1 -0
- package/src/lib/styles/index.shadow.cjs +1 -1
- package/src/lib/styles/index.shadow.js +1 -1
- package/src/lib/utils/hooks/useAleoShieldedBalances/index.d.ts +1 -0
- package/src/lib/utils/hooks/useAleoShieldedBalances/useAleoShieldedBalances.cjs +372 -0
- package/src/lib/utils/hooks/useAleoShieldedBalances/useAleoShieldedBalances.d.ts +24 -0
- package/src/lib/utils/hooks/useAleoShieldedBalances/useAleoShieldedBalances.js +368 -0
- package/src/lib/utils/hooks/useEmbeddedWallet/useEmbeddedWallet.cjs +1 -0
- package/src/lib/utils/hooks/useEmbeddedWallet/useEmbeddedWallet.d.ts +1 -0
- package/src/lib/utils/hooks/useEmbeddedWallet/useEmbeddedWallet.js +1 -0
- package/src/lib/views/SendBalanceView/SendBalanceView.cjs +53 -0
- package/src/lib/views/SendBalanceView/SendBalanceView.js +53 -0
- package/src/lib/widgets/DynamicWidget/components/ActiveWalletBalance/ActiveWalletBalance.cjs +191 -11
- package/src/lib/widgets/DynamicWidget/components/ActiveWalletBalance/ActiveWalletBalance.js +191 -11
- package/src/lib/widgets/DynamicWidget/components/ActiveWalletBalance/TokenBalanceItem/TokenBalanceItem.cjs +5 -2
- package/src/lib/widgets/DynamicWidget/components/ActiveWalletBalance/TokenBalanceItem/TokenBalanceItem.d.ts +10 -1
- package/src/lib/widgets/DynamicWidget/components/ActiveWalletBalance/TokenBalanceItem/TokenBalanceItem.js +5 -2
- package/src/lib/widgets/DynamicWidget/components/ActiveWalletBalance/TokenBalanceItem/index.d.ts +1 -0
- package/src/lib/widgets/DynamicWidget/components/ActiveWalletBalance/TokenBalanceList/TokenBalanceList.cjs +2 -2
- package/src/lib/widgets/DynamicWidget/components/ActiveWalletBalance/TokenBalanceList/TokenBalanceList.d.ts +3 -1
- package/src/lib/widgets/DynamicWidget/components/ActiveWalletBalance/TokenBalanceList/TokenBalanceList.js +2 -2
package/src/lib/widgets/DynamicWidget/components/ActiveWalletBalance/ActiveWalletBalance.cjs
CHANGED
|
@@ -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('
|
|
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
|
-
|
|
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
|
-
}), [
|
|
330
|
+
}), [
|
|
331
|
+
isRefreshing,
|
|
332
|
+
isSuccess,
|
|
333
|
+
fetchAccountBalances,
|
|
334
|
+
refetchShielded,
|
|
335
|
+
supportsShielded,
|
|
336
|
+
activeShieldTab,
|
|
337
|
+
]);
|
|
201
338
|
const primaryWalletNativeBalance = () => {
|
|
202
|
-
if (!primaryWallet ||
|
|
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 ||
|
|
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.
|
|
228
|
-
|
|
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 '
|
|
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
|
-
|
|
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
|
-
}), [
|
|
326
|
+
}), [
|
|
327
|
+
isRefreshing,
|
|
328
|
+
isSuccess,
|
|
329
|
+
fetchAccountBalances,
|
|
330
|
+
refetchShielded,
|
|
331
|
+
supportsShielded,
|
|
332
|
+
activeShieldTab,
|
|
333
|
+
]);
|
|
197
334
|
const primaryWalletNativeBalance = () => {
|
|
198
|
-
if (!primaryWallet ||
|
|
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 ||
|
|
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 (
|
|
224
|
-
|
|
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 })] }) })] })] },
|
|
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
|
|
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;
|