@unifold/ui-react 0.1.41 → 0.1.42
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/dist/index.d.mts +309 -34
- package/dist/index.d.ts +309 -34
- package/dist/index.js +2061 -34
- package/dist/index.mjs +2085 -36
- package/dist/styles-base.css +1 -1
- package/dist/styles.css +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -74,17 +74,31 @@ __export(index_exports, {
|
|
|
74
74
|
TransferCryptoButton: () => TransferCryptoButton,
|
|
75
75
|
TransferCryptoDoubleInput: () => TransferCryptoDoubleInput,
|
|
76
76
|
TransferCryptoSingleInput: () => TransferCryptoSingleInput,
|
|
77
|
+
WithdrawConfirmingView: () => WithdrawConfirmingView,
|
|
78
|
+
WithdrawDoubleInput: () => WithdrawDoubleInput,
|
|
79
|
+
WithdrawExecutionItem: () => WithdrawExecutionItem,
|
|
80
|
+
WithdrawForm: () => WithdrawForm,
|
|
81
|
+
WithdrawModal: () => WithdrawModal,
|
|
82
|
+
WithdrawTokenSelector: () => WithdrawTokenSelector,
|
|
77
83
|
buttonVariants: () => buttonVariants,
|
|
78
84
|
cn: () => cn,
|
|
79
85
|
colors: () => colors,
|
|
80
86
|
defaultColors: () => defaultColors,
|
|
87
|
+
detectBrowserWallet: () => detectBrowserWallet,
|
|
81
88
|
getColors: () => getColors,
|
|
82
89
|
mergeColors: () => mergeColors,
|
|
83
90
|
resolveComponentTokens: () => resolveComponentTokens,
|
|
91
|
+
sendEvmWithdraw: () => sendEvmWithdraw,
|
|
92
|
+
sendSolanaWithdraw: () => sendSolanaWithdraw,
|
|
84
93
|
truncateAddress: () => truncateAddress,
|
|
94
|
+
useAddressBalance: () => useAddressBalance,
|
|
85
95
|
useAllowedCountry: () => useAllowedCountry,
|
|
86
96
|
useDepositPolling: () => useDepositPolling,
|
|
87
|
-
|
|
97
|
+
useSourceTokenValidation: () => useSourceTokenValidation,
|
|
98
|
+
useSupportedDestinationTokens: () => useSupportedDestinationTokens,
|
|
99
|
+
useTheme: () => useTheme,
|
|
100
|
+
useVerifyRecipientAddress: () => useVerifyRecipientAddress,
|
|
101
|
+
useWithdrawPolling: () => useWithdrawPolling
|
|
88
102
|
});
|
|
89
103
|
module.exports = __toCommonJS(index_exports);
|
|
90
104
|
|
|
@@ -693,6 +707,7 @@ function useDepositAddress(params) {
|
|
|
693
707
|
destinationChainType,
|
|
694
708
|
destinationChainId,
|
|
695
709
|
destinationTokenAddress,
|
|
710
|
+
actionType,
|
|
696
711
|
enabled = true
|
|
697
712
|
} = params;
|
|
698
713
|
return (0, import_react_query.useQuery)({
|
|
@@ -704,6 +719,7 @@ function useDepositAddress(params) {
|
|
|
704
719
|
destinationChainType ?? null,
|
|
705
720
|
destinationChainId ?? null,
|
|
706
721
|
destinationTokenAddress ?? null,
|
|
722
|
+
actionType ?? null,
|
|
707
723
|
publishableKey
|
|
708
724
|
],
|
|
709
725
|
queryFn: () => (0, import_core.createDepositAddress)(
|
|
@@ -712,7 +728,8 @@ function useDepositAddress(params) {
|
|
|
712
728
|
recipient_address: recipientAddress,
|
|
713
729
|
destination_chain_type: destinationChainType,
|
|
714
730
|
destination_chain_id: destinationChainId,
|
|
715
|
-
destination_token_address: destinationTokenAddress
|
|
731
|
+
destination_token_address: destinationTokenAddress,
|
|
732
|
+
action_type: actionType
|
|
716
733
|
},
|
|
717
734
|
publishableKey
|
|
718
735
|
),
|
|
@@ -1426,6 +1443,30 @@ var en_default = {
|
|
|
1426
1443
|
youReceive: "You receive",
|
|
1427
1444
|
intentAddressNote: "The wallet address displayed in the payment provider is a temporary deposit address. Your funds will be automatically converted and deposited into your account."
|
|
1428
1445
|
}
|
|
1446
|
+
},
|
|
1447
|
+
withdrawModal: {
|
|
1448
|
+
title: "Withdraw",
|
|
1449
|
+
withdrawCrypto: {
|
|
1450
|
+
title: "Withdraw with Crypto",
|
|
1451
|
+
subtitle: "Send to any wallet address"
|
|
1452
|
+
},
|
|
1453
|
+
selectToken: "Select withdrawal token",
|
|
1454
|
+
receiveToken: "Receive token",
|
|
1455
|
+
receiveChain: "Receive chain",
|
|
1456
|
+
recipientAddress: "Recipient address",
|
|
1457
|
+
recipientAddressPlaceholder: "Enter wallet address",
|
|
1458
|
+
amount: "Amount",
|
|
1459
|
+
amountPlaceholder: "0.00",
|
|
1460
|
+
balance: "Balance",
|
|
1461
|
+
minimum: "min",
|
|
1462
|
+
withdraw: "Withdraw",
|
|
1463
|
+
invalidAddress: "Please enter a valid address",
|
|
1464
|
+
invalidAmount: "Please enter a valid amount",
|
|
1465
|
+
verifyingAddress: "Verifying address...",
|
|
1466
|
+
loading: "Loading...",
|
|
1467
|
+
noTokensAvailable: "No tokens available",
|
|
1468
|
+
review: "Review Withdrawal",
|
|
1469
|
+
back: "Back"
|
|
1429
1470
|
}
|
|
1430
1471
|
};
|
|
1431
1472
|
|
|
@@ -1510,7 +1551,7 @@ function useDepositPolling({
|
|
|
1510
1551
|
const modalOpenedAt = modalOpenedAtRef.current;
|
|
1511
1552
|
const poll = async () => {
|
|
1512
1553
|
try {
|
|
1513
|
-
const response = await (0, import_core6.queryExecutions)(userId, publishableKey);
|
|
1554
|
+
const response = await (0, import_core6.queryExecutions)(userId, publishableKey, import_core6.ActionType.Deposit);
|
|
1514
1555
|
const cutoff = new Date(modalOpenedAt.getTime() - CUTOFF_BUFFER_MS);
|
|
1515
1556
|
const sortedExecutions = [...response.data].sort((a, b) => {
|
|
1516
1557
|
const timeA = a.created_at ? new Date(a.created_at).getTime() : 0;
|
|
@@ -1646,7 +1687,8 @@ function formatCurrency(currency) {
|
|
|
1646
1687
|
}
|
|
1647
1688
|
function DepositDetailContent({
|
|
1648
1689
|
execution,
|
|
1649
|
-
className
|
|
1690
|
+
className,
|
|
1691
|
+
variant = "deposit"
|
|
1650
1692
|
}) {
|
|
1651
1693
|
const { colors: colors2, fonts, components } = useTheme();
|
|
1652
1694
|
const [chains, setChains] = (0, import_react4.useState)([]);
|
|
@@ -2052,7 +2094,7 @@ function DepositDetailContent({
|
|
|
2052
2094
|
color: components.card.rowLeftLabel,
|
|
2053
2095
|
fontFamily: fonts.regular
|
|
2054
2096
|
},
|
|
2055
|
-
children: "Deposit Tx"
|
|
2097
|
+
children: variant === "withdraw" ? "Withdrawal Tx" : "Deposit Tx"
|
|
2056
2098
|
}
|
|
2057
2099
|
),
|
|
2058
2100
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
@@ -7338,7 +7380,7 @@ function DepositsModal({
|
|
|
7338
7380
|
if (!open || !userId) return;
|
|
7339
7381
|
const fetchExecutions = async () => {
|
|
7340
7382
|
try {
|
|
7341
|
-
const response = await (0, import_core19.queryExecutions)(userId, publishableKey);
|
|
7383
|
+
const response = await (0, import_core19.queryExecutions)(userId, publishableKey, import_core19.ActionType.Deposit);
|
|
7342
7384
|
const sorted = [...response.data].sort((a, b) => {
|
|
7343
7385
|
const timeA = a.created_at ? new Date(a.created_at).getTime() : 0;
|
|
7344
7386
|
const timeB = b.created_at ? new Date(b.created_at).getTime() : 0;
|
|
@@ -7460,7 +7502,7 @@ function saveRecentToken(token) {
|
|
|
7460
7502
|
try {
|
|
7461
7503
|
const recent = getRecentTokens();
|
|
7462
7504
|
const filtered = recent.filter(
|
|
7463
|
-
(
|
|
7505
|
+
(t11) => !(t11.symbol === token.symbol && t11.chainType === token.chainType && t11.chainId === token.chainId)
|
|
7464
7506
|
);
|
|
7465
7507
|
filtered.unshift(token);
|
|
7466
7508
|
const trimmed = filtered.slice(0, MAX_RECENT_TOKENS);
|
|
@@ -7473,7 +7515,7 @@ function removeRecentToken(token) {
|
|
|
7473
7515
|
try {
|
|
7474
7516
|
const recent = getRecentTokens();
|
|
7475
7517
|
const filtered = recent.filter(
|
|
7476
|
-
(
|
|
7518
|
+
(t11) => !(t11.symbol === token.symbol && t11.chainType === token.chainType && t11.chainId === token.chainId)
|
|
7477
7519
|
);
|
|
7478
7520
|
localStorage.setItem(STORAGE_KEY, JSON.stringify(filtered));
|
|
7479
7521
|
return filtered;
|
|
@@ -7512,7 +7554,7 @@ function TokenSelectorSheet({
|
|
|
7512
7554
|
const addOption = (symbol, chainType, chainId, isRecent) => {
|
|
7513
7555
|
const key = `${symbol}-${chainType}:${chainId}`;
|
|
7514
7556
|
if (seen.has(key)) return;
|
|
7515
|
-
const tokenData = tokens.find((
|
|
7557
|
+
const tokenData = tokens.find((t11) => t11.symbol === symbol);
|
|
7516
7558
|
if (!tokenData) return;
|
|
7517
7559
|
const chainData = tokenData.chains.find(
|
|
7518
7560
|
(c) => c.chain_type === chainType && c.chain_id === chainId
|
|
@@ -7983,35 +8025,35 @@ function resolveSourceToken(supportedTokens, defaultSourceChainType, defaultSour
|
|
|
7983
8025
|
let selectedChainData;
|
|
7984
8026
|
const hasChainDefaults = defaultSourceChainType && defaultSourceChainId;
|
|
7985
8027
|
if (defaultSourceTokenAddress && hasChainDefaults) {
|
|
7986
|
-
for (const
|
|
7987
|
-
const matchingChain =
|
|
8028
|
+
for (const t11 of supportedTokens) {
|
|
8029
|
+
const matchingChain = t11.chains.find(
|
|
7988
8030
|
(c) => c.token_address.toLowerCase() === defaultSourceTokenAddress.toLowerCase() && c.chain_type === defaultSourceChainType && c.chain_id === defaultSourceChainId
|
|
7989
8031
|
);
|
|
7990
8032
|
if (matchingChain) {
|
|
7991
|
-
selectedTokenData =
|
|
8033
|
+
selectedTokenData = t11;
|
|
7992
8034
|
selectedChainData = matchingChain;
|
|
7993
8035
|
break;
|
|
7994
8036
|
}
|
|
7995
8037
|
}
|
|
7996
8038
|
}
|
|
7997
8039
|
if (!selectedTokenData && defaultSourceSymbol && hasChainDefaults) {
|
|
7998
|
-
for (const
|
|
7999
|
-
if (
|
|
8000
|
-
const matchedChain =
|
|
8040
|
+
for (const t11 of supportedTokens) {
|
|
8041
|
+
if (t11.symbol !== defaultSourceSymbol) continue;
|
|
8042
|
+
const matchedChain = t11.chains.find(
|
|
8001
8043
|
(c) => c.chain_type === defaultSourceChainType && c.chain_id === defaultSourceChainId
|
|
8002
8044
|
);
|
|
8003
8045
|
if (matchedChain) {
|
|
8004
|
-
selectedTokenData =
|
|
8046
|
+
selectedTokenData = t11;
|
|
8005
8047
|
selectedChainData = matchedChain;
|
|
8006
8048
|
break;
|
|
8007
8049
|
}
|
|
8008
8050
|
}
|
|
8009
8051
|
}
|
|
8010
8052
|
if (!selectedTokenData) {
|
|
8011
|
-
for (const
|
|
8012
|
-
if (
|
|
8013
|
-
selectedTokenData =
|
|
8014
|
-
selectedChainData =
|
|
8053
|
+
for (const t11 of supportedTokens) {
|
|
8054
|
+
if (t11.chains.length > 0) {
|
|
8055
|
+
selectedTokenData = t11;
|
|
8056
|
+
selectedChainData = t11.chains[0];
|
|
8015
8057
|
break;
|
|
8016
8058
|
}
|
|
8017
8059
|
}
|
|
@@ -8060,7 +8102,7 @@ function useDefaultSourceToken({
|
|
|
8060
8102
|
]);
|
|
8061
8103
|
(0, import_react13.useEffect)(() => {
|
|
8062
8104
|
if (!supportedTokens.length || !token) return;
|
|
8063
|
-
const currentToken = supportedTokens.find((
|
|
8105
|
+
const currentToken = supportedTokens.find((t11) => t11.symbol === token);
|
|
8064
8106
|
if (!currentToken || currentToken.chains.length === 0) return;
|
|
8065
8107
|
const isChainAvailable = chain && currentToken.chains.some((c) => {
|
|
8066
8108
|
return getChainKey(c.chain_id, c.chain_type) === chain;
|
|
@@ -8384,8 +8426,8 @@ function TransferCryptoSingleInput({
|
|
|
8384
8426
|
const error = walletsError?.message ?? null;
|
|
8385
8427
|
const allAvailableChains = (0, import_react15.useMemo)(() => {
|
|
8386
8428
|
const chainsMap = /* @__PURE__ */ new Map();
|
|
8387
|
-
supportedTokens.forEach((
|
|
8388
|
-
|
|
8429
|
+
supportedTokens.forEach((t11) => {
|
|
8430
|
+
t11.chains.forEach((c) => {
|
|
8389
8431
|
const comboKey = `${c.chain_type}:${c.chain_id}`;
|
|
8390
8432
|
if (!chainsMap.has(comboKey)) {
|
|
8391
8433
|
chainsMap.set(comboKey, c);
|
|
@@ -8420,7 +8462,7 @@ function TransferCryptoSingleInput({
|
|
|
8420
8462
|
onExecutionsChange(depositExecutions);
|
|
8421
8463
|
}
|
|
8422
8464
|
}, [depositExecutions, onExecutionsChange]);
|
|
8423
|
-
const selectedToken = token ? supportedTokens.find((
|
|
8465
|
+
const selectedToken = token ? supportedTokens.find((t11) => t11.symbol === token) : void 0;
|
|
8424
8466
|
const availableChainsForToken = selectedToken?.chains || [];
|
|
8425
8467
|
const currentChainFromBackend = chain ? availableChainsForToken.find((c) => {
|
|
8426
8468
|
const key = getChainKey2(c.chain_id, c.chain_type);
|
|
@@ -8434,7 +8476,7 @@ function TransferCryptoSingleInput({
|
|
|
8434
8476
|
setCopied(true);
|
|
8435
8477
|
setTimeout(() => setCopied(false), 2e3);
|
|
8436
8478
|
};
|
|
8437
|
-
const
|
|
8479
|
+
const formatProcessingTime3 = (seconds) => {
|
|
8438
8480
|
if (seconds === null) {
|
|
8439
8481
|
return t4.processingTime.lessThanMinutes.replace("{{minutes}}", "1");
|
|
8440
8482
|
}
|
|
@@ -8643,7 +8685,7 @@ function TransferCryptoSingleInput({
|
|
|
8643
8685
|
t4.processingTime.label,
|
|
8644
8686
|
":",
|
|
8645
8687
|
" ",
|
|
8646
|
-
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)("span", { style: { color: components.card.titleColor, fontFamily: fonts.medium }, children:
|
|
8688
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)("span", { style: { color: components.card.titleColor, fontFamily: fonts.medium }, children: formatProcessingTime3(processingTime) })
|
|
8647
8689
|
] })
|
|
8648
8690
|
] }),
|
|
8649
8691
|
detailsExpanded ? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_lucide_react16.ChevronUp, { className: "uf-w-4 uf-h-4", style: { color: components.card.actionColor } }) : /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_lucide_react16.ChevronDown, { className: "uf-w-4 uf-h-4", style: { color: components.card.actionColor } })
|
|
@@ -8957,8 +8999,8 @@ function TransferCryptoDoubleInput({
|
|
|
8957
8999
|
const error = walletsError?.message ?? null;
|
|
8958
9000
|
const allAvailableChains = (0, import_react16.useMemo)(() => {
|
|
8959
9001
|
const chainsMap = /* @__PURE__ */ new Map();
|
|
8960
|
-
supportedTokens.forEach((
|
|
8961
|
-
|
|
9002
|
+
supportedTokens.forEach((t11) => {
|
|
9003
|
+
t11.chains.forEach((c) => {
|
|
8962
9004
|
const comboKey = `${c.chain_type}:${c.chain_id}`;
|
|
8963
9005
|
if (!chainsMap.has(comboKey)) {
|
|
8964
9006
|
chainsMap.set(comboKey, c);
|
|
@@ -8993,7 +9035,7 @@ function TransferCryptoDoubleInput({
|
|
|
8993
9035
|
onExecutionsChange(depositExecutions);
|
|
8994
9036
|
}
|
|
8995
9037
|
}, [depositExecutions, onExecutionsChange]);
|
|
8996
|
-
const selectedToken = token ? supportedTokens.find((
|
|
9038
|
+
const selectedToken = token ? supportedTokens.find((t11) => t11.symbol === token) : void 0;
|
|
8997
9039
|
const availableChainsForToken = selectedToken?.chains || [];
|
|
8998
9040
|
const currentChainFromBackend = chain ? availableChainsForToken.find((c) => {
|
|
8999
9041
|
const key = getChainKey3(c.chain_id, c.chain_type);
|
|
@@ -9007,7 +9049,7 @@ function TransferCryptoDoubleInput({
|
|
|
9007
9049
|
setCopied(true);
|
|
9008
9050
|
setTimeout(() => setCopied(false), 2e3);
|
|
9009
9051
|
};
|
|
9010
|
-
const
|
|
9052
|
+
const formatProcessingTime3 = (seconds) => {
|
|
9011
9053
|
if (seconds === null) {
|
|
9012
9054
|
return t5.processingTime.lessThanMinutes.replace("{{minutes}}", "1");
|
|
9013
9055
|
}
|
|
@@ -9234,7 +9276,7 @@ function TransferCryptoDoubleInput({
|
|
|
9234
9276
|
t5.processingTime.label,
|
|
9235
9277
|
":",
|
|
9236
9278
|
" ",
|
|
9237
|
-
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)("span", { style: { color: components.card.titleColor, fontFamily: fonts.medium }, children:
|
|
9279
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)("span", { style: { color: components.card.titleColor, fontFamily: fonts.medium }, children: formatProcessingTime3(processingTime) })
|
|
9238
9280
|
] })
|
|
9239
9281
|
] }),
|
|
9240
9282
|
detailsExpanded ? /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_lucide_react18.ChevronUp, { className: "uf-w-4 uf-h-4", style: { color: components.card.actionColor } }) : /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_lucide_react18.ChevronDown, { className: "uf-w-4 uf-h-4", style: { color: components.card.actionColor } })
|
|
@@ -10308,7 +10350,7 @@ function BrowserWalletModal({
|
|
|
10308
10350
|
);
|
|
10309
10351
|
if (cancelled) return;
|
|
10310
10352
|
const supportedToken = response.data.find(
|
|
10311
|
-
(
|
|
10353
|
+
(t11) => t11.symbol.toLowerCase() === token.symbol.toLowerCase()
|
|
10312
10354
|
);
|
|
10313
10355
|
if (supportedToken) {
|
|
10314
10356
|
const chainDetail = supportedToken.chains.find(
|
|
@@ -11624,7 +11666,7 @@ function DepositModal({
|
|
|
11624
11666
|
if (view !== "tracker" || !userId) return;
|
|
11625
11667
|
const fetchExecutions = async () => {
|
|
11626
11668
|
try {
|
|
11627
|
-
const response = await (0, import_core23.queryExecutions)(userId, publishableKey);
|
|
11669
|
+
const response = await (0, import_core23.queryExecutions)(userId, publishableKey, import_core23.ActionType.Deposit);
|
|
11628
11670
|
const sorted = [...response.data].sort((a, b) => {
|
|
11629
11671
|
const timeA = a.created_at ? new Date(a.created_at).getTime() : 0;
|
|
11630
11672
|
const timeB = b.created_at ? new Date(b.created_at).getTime() : 0;
|
|
@@ -12143,6 +12185,1977 @@ function DepositModal({
|
|
|
12143
12185
|
}
|
|
12144
12186
|
) });
|
|
12145
12187
|
}
|
|
12188
|
+
|
|
12189
|
+
// src/components/withdrawals/WithdrawModal.tsx
|
|
12190
|
+
var import_react22 = require("react");
|
|
12191
|
+
var import_lucide_react26 = require("lucide-react");
|
|
12192
|
+
|
|
12193
|
+
// src/hooks/use-supported-destination-tokens.ts
|
|
12194
|
+
var import_react_query9 = require("@tanstack/react-query");
|
|
12195
|
+
var import_core24 = require("@unifold/core");
|
|
12196
|
+
function useSupportedDestinationTokens(publishableKey, enabled = true) {
|
|
12197
|
+
return (0, import_react_query9.useQuery)({
|
|
12198
|
+
queryKey: ["unifold", "supportedDestinationTokens", publishableKey],
|
|
12199
|
+
queryFn: () => (0, import_core24.getSupportedDestinationTokens)(publishableKey),
|
|
12200
|
+
staleTime: 1e3 * 60 * 5,
|
|
12201
|
+
gcTime: 1e3 * 60 * 30,
|
|
12202
|
+
refetchOnMount: false,
|
|
12203
|
+
refetchOnWindowFocus: false,
|
|
12204
|
+
enabled
|
|
12205
|
+
});
|
|
12206
|
+
}
|
|
12207
|
+
|
|
12208
|
+
// src/hooks/use-source-token-validation.ts
|
|
12209
|
+
var import_react_query10 = require("@tanstack/react-query");
|
|
12210
|
+
var import_core25 = require("@unifold/core");
|
|
12211
|
+
function useSourceTokenValidation(params) {
|
|
12212
|
+
const {
|
|
12213
|
+
sourceChainType,
|
|
12214
|
+
sourceChainId,
|
|
12215
|
+
sourceTokenAddress,
|
|
12216
|
+
sourceTokenSymbol,
|
|
12217
|
+
publishableKey,
|
|
12218
|
+
enabled = true
|
|
12219
|
+
} = params;
|
|
12220
|
+
const hasParams = !!sourceChainType && !!sourceChainId && !!sourceTokenAddress;
|
|
12221
|
+
return (0, import_react_query10.useQuery)({
|
|
12222
|
+
queryKey: [
|
|
12223
|
+
"unifold",
|
|
12224
|
+
"sourceTokenValidation",
|
|
12225
|
+
sourceChainType ?? null,
|
|
12226
|
+
sourceChainId ?? null,
|
|
12227
|
+
sourceTokenAddress ?? null,
|
|
12228
|
+
publishableKey
|
|
12229
|
+
],
|
|
12230
|
+
queryFn: async () => {
|
|
12231
|
+
const res = await (0, import_core25.getSupportedDepositTokens)(publishableKey);
|
|
12232
|
+
let matchedMinUsd = null;
|
|
12233
|
+
let matchedProcessingTime = null;
|
|
12234
|
+
let matchedSlippage = null;
|
|
12235
|
+
let matchedPriceImpact = null;
|
|
12236
|
+
const found = res.data.some(
|
|
12237
|
+
(token) => token.chains.some((chain) => {
|
|
12238
|
+
const match = chain.chain_type === sourceChainType && chain.chain_id === sourceChainId && chain.token_address.toLowerCase() === sourceTokenAddress.toLowerCase();
|
|
12239
|
+
if (match) {
|
|
12240
|
+
matchedMinUsd = chain.minimum_deposit_amount_usd;
|
|
12241
|
+
matchedProcessingTime = chain.estimated_processing_time;
|
|
12242
|
+
matchedSlippage = chain.max_slippage_percent;
|
|
12243
|
+
matchedPriceImpact = chain.estimated_price_impact_percent;
|
|
12244
|
+
}
|
|
12245
|
+
return match;
|
|
12246
|
+
})
|
|
12247
|
+
);
|
|
12248
|
+
return {
|
|
12249
|
+
isSupported: found,
|
|
12250
|
+
minimumAmountUsd: matchedMinUsd,
|
|
12251
|
+
estimatedProcessingTime: matchedProcessingTime,
|
|
12252
|
+
maxSlippagePercent: matchedSlippage,
|
|
12253
|
+
priceImpactPercent: matchedPriceImpact,
|
|
12254
|
+
errorMessage: found ? null : `${sourceTokenSymbol || "Source token"} is not a supported withdrawal token. Supported tokens include USDC, USDT, and other stablecoins.`
|
|
12255
|
+
};
|
|
12256
|
+
},
|
|
12257
|
+
enabled: enabled && hasParams,
|
|
12258
|
+
staleTime: 1e3 * 60 * 5,
|
|
12259
|
+
gcTime: 1e3 * 60 * 30,
|
|
12260
|
+
refetchOnMount: false,
|
|
12261
|
+
refetchOnWindowFocus: false
|
|
12262
|
+
});
|
|
12263
|
+
}
|
|
12264
|
+
|
|
12265
|
+
// src/hooks/use-address-balance.ts
|
|
12266
|
+
var import_react_query11 = require("@tanstack/react-query");
|
|
12267
|
+
var import_core26 = require("@unifold/core");
|
|
12268
|
+
function useAddressBalance(params) {
|
|
12269
|
+
const {
|
|
12270
|
+
address,
|
|
12271
|
+
chainType,
|
|
12272
|
+
chainId,
|
|
12273
|
+
tokenAddress,
|
|
12274
|
+
publishableKey,
|
|
12275
|
+
enabled = true
|
|
12276
|
+
} = params;
|
|
12277
|
+
const hasParams = !!address && !!chainType && !!chainId && !!tokenAddress;
|
|
12278
|
+
return (0, import_react_query11.useQuery)({
|
|
12279
|
+
queryKey: [
|
|
12280
|
+
"unifold",
|
|
12281
|
+
"addressBalance",
|
|
12282
|
+
address ?? null,
|
|
12283
|
+
chainType ?? null,
|
|
12284
|
+
chainId ?? null,
|
|
12285
|
+
tokenAddress ?? null,
|
|
12286
|
+
publishableKey
|
|
12287
|
+
],
|
|
12288
|
+
queryFn: async () => {
|
|
12289
|
+
const res = await (0, import_core26.getAddressBalance)(
|
|
12290
|
+
address,
|
|
12291
|
+
chainType,
|
|
12292
|
+
chainId,
|
|
12293
|
+
tokenAddress,
|
|
12294
|
+
publishableKey
|
|
12295
|
+
);
|
|
12296
|
+
if (res.balance) {
|
|
12297
|
+
const decimals = res.balance.token?.decimals ?? 6;
|
|
12298
|
+
const symbol = res.balance.token?.symbol ?? "";
|
|
12299
|
+
const baseUnit = res.balance.amount;
|
|
12300
|
+
const raw = BigInt(baseUnit);
|
|
12301
|
+
const divisor = BigInt(10 ** decimals);
|
|
12302
|
+
const whole = raw / divisor;
|
|
12303
|
+
const frac = raw % divisor;
|
|
12304
|
+
const fracStr = frac.toString().padStart(decimals, "0").replace(/0+$/, "");
|
|
12305
|
+
const balanceHuman = fracStr ? `${whole}.${fracStr}` : whole.toString();
|
|
12306
|
+
return {
|
|
12307
|
+
balanceBaseUnit: baseUnit,
|
|
12308
|
+
balanceHuman,
|
|
12309
|
+
balanceUsd: res.balance.amount_usd,
|
|
12310
|
+
exchangeRate: res.balance.exchange_rate,
|
|
12311
|
+
decimals,
|
|
12312
|
+
symbol
|
|
12313
|
+
};
|
|
12314
|
+
}
|
|
12315
|
+
return { balanceBaseUnit: "0", balanceHuman: "0", balanceUsd: "0", exchangeRate: null, decimals: 6, symbol: "" };
|
|
12316
|
+
},
|
|
12317
|
+
enabled: enabled && hasParams,
|
|
12318
|
+
staleTime: 1e3 * 30,
|
|
12319
|
+
gcTime: 1e3 * 60 * 5,
|
|
12320
|
+
refetchInterval: 1e3 * 30,
|
|
12321
|
+
refetchOnMount: "always",
|
|
12322
|
+
refetchOnWindowFocus: false
|
|
12323
|
+
});
|
|
12324
|
+
}
|
|
12325
|
+
|
|
12326
|
+
// src/hooks/use-executions.ts
|
|
12327
|
+
var import_react_query12 = require("@tanstack/react-query");
|
|
12328
|
+
var import_core27 = require("@unifold/core");
|
|
12329
|
+
function useExecutions(userId, publishableKey, options) {
|
|
12330
|
+
const actionType = options?.actionType ?? import_core27.ActionType.Deposit;
|
|
12331
|
+
return (0, import_react_query12.useQuery)({
|
|
12332
|
+
queryKey: ["unifold", "executions", actionType, userId, publishableKey],
|
|
12333
|
+
queryFn: () => (0, import_core27.queryExecutions)(userId, publishableKey, actionType),
|
|
12334
|
+
enabled: (options?.enabled ?? true) && !!userId,
|
|
12335
|
+
refetchInterval: options?.refetchInterval ?? 3e3,
|
|
12336
|
+
staleTime: 0,
|
|
12337
|
+
gcTime: 1e3 * 60 * 5,
|
|
12338
|
+
refetchOnWindowFocus: false
|
|
12339
|
+
});
|
|
12340
|
+
}
|
|
12341
|
+
|
|
12342
|
+
// src/hooks/use-withdraw-polling.ts
|
|
12343
|
+
var import_react19 = require("react");
|
|
12344
|
+
var import_core28 = require("@unifold/core");
|
|
12345
|
+
var POLL_INTERVAL_MS2 = 2500;
|
|
12346
|
+
var POLL_ENDPOINT_INTERVAL_MS2 = 3e3;
|
|
12347
|
+
var CUTOFF_BUFFER_MS2 = 6e4;
|
|
12348
|
+
function useWithdrawPolling({
|
|
12349
|
+
userId,
|
|
12350
|
+
publishableKey,
|
|
12351
|
+
depositWalletId,
|
|
12352
|
+
enabled = false,
|
|
12353
|
+
onWithdrawSuccess,
|
|
12354
|
+
onWithdrawError
|
|
12355
|
+
}) {
|
|
12356
|
+
const [executions, setExecutions] = (0, import_react19.useState)([]);
|
|
12357
|
+
const [isPolling, setIsPolling] = (0, import_react19.useState)(false);
|
|
12358
|
+
const enabledAtRef = (0, import_react19.useRef)(/* @__PURE__ */ new Date());
|
|
12359
|
+
const trackedRef = (0, import_react19.useRef)(/* @__PURE__ */ new Map());
|
|
12360
|
+
const prevEnabledRef = (0, import_react19.useRef)(false);
|
|
12361
|
+
const onSuccessRef = (0, import_react19.useRef)(onWithdrawSuccess);
|
|
12362
|
+
const onErrorRef = (0, import_react19.useRef)(onWithdrawError);
|
|
12363
|
+
(0, import_react19.useEffect)(() => {
|
|
12364
|
+
onSuccessRef.current = onWithdrawSuccess;
|
|
12365
|
+
}, [onWithdrawSuccess]);
|
|
12366
|
+
(0, import_react19.useEffect)(() => {
|
|
12367
|
+
onErrorRef.current = onWithdrawError;
|
|
12368
|
+
}, [onWithdrawError]);
|
|
12369
|
+
(0, import_react19.useEffect)(() => {
|
|
12370
|
+
if (enabled && !prevEnabledRef.current) {
|
|
12371
|
+
enabledAtRef.current = /* @__PURE__ */ new Date();
|
|
12372
|
+
trackedRef.current.clear();
|
|
12373
|
+
}
|
|
12374
|
+
if (!enabled) {
|
|
12375
|
+
trackedRef.current.clear();
|
|
12376
|
+
}
|
|
12377
|
+
prevEnabledRef.current = enabled;
|
|
12378
|
+
}, [enabled]);
|
|
12379
|
+
(0, import_react19.useEffect)(() => {
|
|
12380
|
+
if (!userId || !enabled) return;
|
|
12381
|
+
const enabledAt = enabledAtRef.current;
|
|
12382
|
+
const poll = async () => {
|
|
12383
|
+
try {
|
|
12384
|
+
const response = await (0, import_core28.queryExecutions)(userId, publishableKey, import_core28.ActionType.Withdraw);
|
|
12385
|
+
const cutoff = new Date(enabledAt.getTime() - CUTOFF_BUFFER_MS2);
|
|
12386
|
+
const sorted = [...response.data].sort((a, b) => {
|
|
12387
|
+
const tA = a.created_at ? new Date(a.created_at).getTime() : 0;
|
|
12388
|
+
const tB = b.created_at ? new Date(b.created_at).getTime() : 0;
|
|
12389
|
+
return tB - tA;
|
|
12390
|
+
});
|
|
12391
|
+
const inProgress = [import_core28.ExecutionStatus.PENDING, import_core28.ExecutionStatus.WAITING, import_core28.ExecutionStatus.DELAYED];
|
|
12392
|
+
const terminal = [import_core28.ExecutionStatus.SUCCEEDED, import_core28.ExecutionStatus.FAILED];
|
|
12393
|
+
let target = null;
|
|
12394
|
+
for (const ex of sorted) {
|
|
12395
|
+
const t11 = ex.created_at ? new Date(ex.created_at) : null;
|
|
12396
|
+
if (!t11 || t11 < cutoff) continue;
|
|
12397
|
+
const prev = trackedRef.current.get(ex.id);
|
|
12398
|
+
if (!prev) {
|
|
12399
|
+
target = ex;
|
|
12400
|
+
break;
|
|
12401
|
+
}
|
|
12402
|
+
if (inProgress.includes(prev) && terminal.includes(ex.status)) {
|
|
12403
|
+
target = ex;
|
|
12404
|
+
break;
|
|
12405
|
+
}
|
|
12406
|
+
}
|
|
12407
|
+
if (target) {
|
|
12408
|
+
const ex = target;
|
|
12409
|
+
const exTime = ex.created_at ? new Date(ex.created_at) : null;
|
|
12410
|
+
if (!exTime || exTime < enabledAtRef.current) return;
|
|
12411
|
+
const prev = trackedRef.current.get(ex.id);
|
|
12412
|
+
trackedRef.current.set(ex.id, ex.status);
|
|
12413
|
+
setExecutions((list) => {
|
|
12414
|
+
const idx = list.findIndex((e) => e.id === ex.id);
|
|
12415
|
+
if (idx >= 0) {
|
|
12416
|
+
const u = [...list];
|
|
12417
|
+
u[idx] = ex;
|
|
12418
|
+
return u;
|
|
12419
|
+
}
|
|
12420
|
+
return [...list, ex];
|
|
12421
|
+
});
|
|
12422
|
+
if (ex.status === import_core28.ExecutionStatus.SUCCEEDED && (!prev || inProgress.includes(prev))) {
|
|
12423
|
+
onSuccessRef.current?.({ message: "Withdrawal completed successfully", executionId: ex.id, transaction: ex });
|
|
12424
|
+
} else if (ex.status === import_core28.ExecutionStatus.FAILED && prev !== import_core28.ExecutionStatus.FAILED) {
|
|
12425
|
+
onErrorRef.current?.({ message: "Withdrawal failed", code: "WITHDRAW_FAILED", error: ex });
|
|
12426
|
+
}
|
|
12427
|
+
}
|
|
12428
|
+
} catch (error) {
|
|
12429
|
+
console.error("Failed to fetch withdraw executions:", error);
|
|
12430
|
+
onErrorRef.current?.({ message: "Failed to fetch withdrawal status", code: "POLLING_ERROR", error });
|
|
12431
|
+
}
|
|
12432
|
+
};
|
|
12433
|
+
void poll();
|
|
12434
|
+
const interval = setInterval(poll, POLL_INTERVAL_MS2);
|
|
12435
|
+
setIsPolling(true);
|
|
12436
|
+
return () => {
|
|
12437
|
+
clearInterval(interval);
|
|
12438
|
+
setIsPolling(false);
|
|
12439
|
+
};
|
|
12440
|
+
}, [userId, publishableKey, enabled]);
|
|
12441
|
+
(0, import_react19.useEffect)(() => {
|
|
12442
|
+
if (!enabled || !depositWalletId) return;
|
|
12443
|
+
const trigger = async () => {
|
|
12444
|
+
try {
|
|
12445
|
+
await (0, import_core28.pollDirectExecutions)({ deposit_wallet_id: depositWalletId }, publishableKey);
|
|
12446
|
+
} catch {
|
|
12447
|
+
}
|
|
12448
|
+
};
|
|
12449
|
+
trigger();
|
|
12450
|
+
const interval = setInterval(trigger, POLL_ENDPOINT_INTERVAL_MS2);
|
|
12451
|
+
return () => clearInterval(interval);
|
|
12452
|
+
}, [enabled, depositWalletId, publishableKey]);
|
|
12453
|
+
return { executions, isPolling };
|
|
12454
|
+
}
|
|
12455
|
+
|
|
12456
|
+
// src/components/withdrawals/WithdrawDoubleInput.tsx
|
|
12457
|
+
var import_jsx_runtime52 = require("react/jsx-runtime");
|
|
12458
|
+
var t7 = i18n.withdrawModal;
|
|
12459
|
+
var getChainKey4 = (chainId, chainType) => `${chainType}:${chainId}`;
|
|
12460
|
+
function WithdrawDoubleInput({
|
|
12461
|
+
tokens,
|
|
12462
|
+
selectedTokenSymbol,
|
|
12463
|
+
selectedChainKey,
|
|
12464
|
+
onTokenChange,
|
|
12465
|
+
onChainChange,
|
|
12466
|
+
isLoading = false
|
|
12467
|
+
}) {
|
|
12468
|
+
const { fonts, components } = useTheme();
|
|
12469
|
+
const isDarkMode = useTheme().themeClass.includes("uf-dark");
|
|
12470
|
+
const selectedToken = selectedTokenSymbol ? tokens.find((t11) => t11.symbol === selectedTokenSymbol) : void 0;
|
|
12471
|
+
const availableChainsForToken = selectedToken?.chains || [];
|
|
12472
|
+
const renderTokenItem = (tokenData) => /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)("div", { className: "uf-flex uf-items-center uf-gap-2", children: [
|
|
12473
|
+
/* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
|
|
12474
|
+
"img",
|
|
12475
|
+
{
|
|
12476
|
+
src: tokenData.icon_url,
|
|
12477
|
+
alt: tokenData.symbol,
|
|
12478
|
+
width: 20,
|
|
12479
|
+
height: 20,
|
|
12480
|
+
loading: "lazy",
|
|
12481
|
+
className: "uf-rounded-full uf-flex-shrink-0"
|
|
12482
|
+
}
|
|
12483
|
+
),
|
|
12484
|
+
/* @__PURE__ */ (0, import_jsx_runtime52.jsx)("span", { className: "uf-text-xs uf-font-normal", children: tokenData.symbol })
|
|
12485
|
+
] });
|
|
12486
|
+
const renderChainItem = (chainData) => /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)("div", { className: "uf-flex uf-items-center uf-gap-2", children: [
|
|
12487
|
+
/* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
|
|
12488
|
+
"img",
|
|
12489
|
+
{
|
|
12490
|
+
src: chainData.icon_url,
|
|
12491
|
+
alt: chainData.chain_name,
|
|
12492
|
+
width: 20,
|
|
12493
|
+
height: 20,
|
|
12494
|
+
loading: "lazy",
|
|
12495
|
+
className: "uf-rounded-full uf-flex-shrink-0"
|
|
12496
|
+
}
|
|
12497
|
+
),
|
|
12498
|
+
/* @__PURE__ */ (0, import_jsx_runtime52.jsx)("span", { className: "uf-text-xs uf-font-normal", children: chainData.chain_name })
|
|
12499
|
+
] });
|
|
12500
|
+
const currentChainData = selectedChainKey ? availableChainsForToken.find(
|
|
12501
|
+
(c) => getChainKey4(c.chain_id, c.chain_type) === selectedChainKey
|
|
12502
|
+
) : void 0;
|
|
12503
|
+
return /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)("div", { className: "uf-grid uf-grid-cols-2 uf-gap-2.5", children: [
|
|
12504
|
+
/* @__PURE__ */ (0, import_jsx_runtime52.jsxs)("div", { children: [
|
|
12505
|
+
/* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
|
|
12506
|
+
"div",
|
|
12507
|
+
{
|
|
12508
|
+
className: "uf-text-xs uf-mb-2 uf-flex uf-items-center uf-gap-1",
|
|
12509
|
+
style: { color: components.card.labelColor },
|
|
12510
|
+
children: t7.receiveToken
|
|
12511
|
+
}
|
|
12512
|
+
),
|
|
12513
|
+
/* @__PURE__ */ (0, import_jsx_runtime52.jsxs)(
|
|
12514
|
+
Select,
|
|
12515
|
+
{
|
|
12516
|
+
value: selectedTokenSymbol ?? "",
|
|
12517
|
+
onValueChange: onTokenChange,
|
|
12518
|
+
disabled: isLoading || tokens.length === 0,
|
|
12519
|
+
children: [
|
|
12520
|
+
/* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
|
|
12521
|
+
SelectTrigger,
|
|
12522
|
+
{
|
|
12523
|
+
className: "uf-h-10 hover:uf-opacity-90 uf-text-foreground disabled:uf-opacity-50",
|
|
12524
|
+
style: {
|
|
12525
|
+
backgroundColor: components.card.backgroundColor,
|
|
12526
|
+
border: `${components.card.borderWidth}px solid ${components.card.borderColor}`
|
|
12527
|
+
},
|
|
12528
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(SelectValue, { children: isLoading || !selectedTokenSymbol ? /* @__PURE__ */ (0, import_jsx_runtime52.jsx)("span", { className: "uf-text-xs uf-font-light uf-text-muted-foreground", children: t7.loading }) : selectedToken ? renderTokenItem(selectedToken) : /* @__PURE__ */ (0, import_jsx_runtime52.jsx)("span", { className: "uf-text-xs uf-font-normal", children: selectedTokenSymbol }) })
|
|
12529
|
+
}
|
|
12530
|
+
),
|
|
12531
|
+
/* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
|
|
12532
|
+
SelectContent,
|
|
12533
|
+
{
|
|
12534
|
+
className: "uf-bg-secondary uf-border uf-text-foreground uf-max-h-[300px]",
|
|
12535
|
+
style: {
|
|
12536
|
+
border: `1px solid ${isDarkMode ? "rgba(255,255,255,0.15)" : "rgba(0,0,0,0.15)"}`,
|
|
12537
|
+
...fonts.regular ? { "--uf-font-family": fonts.regular } : {}
|
|
12538
|
+
},
|
|
12539
|
+
children: tokens.map((tokenData) => /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
|
|
12540
|
+
SelectItem,
|
|
12541
|
+
{
|
|
12542
|
+
value: tokenData.symbol,
|
|
12543
|
+
className: "focus:uf-bg-accent focus:uf-text-foreground",
|
|
12544
|
+
children: renderTokenItem(tokenData)
|
|
12545
|
+
},
|
|
12546
|
+
tokenData.symbol
|
|
12547
|
+
))
|
|
12548
|
+
}
|
|
12549
|
+
)
|
|
12550
|
+
]
|
|
12551
|
+
}
|
|
12552
|
+
)
|
|
12553
|
+
] }),
|
|
12554
|
+
/* @__PURE__ */ (0, import_jsx_runtime52.jsxs)("div", { children: [
|
|
12555
|
+
/* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
|
|
12556
|
+
"div",
|
|
12557
|
+
{
|
|
12558
|
+
className: "uf-text-xs uf-mb-2 uf-flex uf-items-center uf-gap-1",
|
|
12559
|
+
style: { color: components.card.labelColor },
|
|
12560
|
+
children: t7.receiveChain
|
|
12561
|
+
}
|
|
12562
|
+
),
|
|
12563
|
+
/* @__PURE__ */ (0, import_jsx_runtime52.jsxs)(
|
|
12564
|
+
Select,
|
|
12565
|
+
{
|
|
12566
|
+
value: selectedChainKey ?? "",
|
|
12567
|
+
onValueChange: onChainChange,
|
|
12568
|
+
disabled: isLoading || availableChainsForToken.length === 0,
|
|
12569
|
+
children: [
|
|
12570
|
+
/* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
|
|
12571
|
+
SelectTrigger,
|
|
12572
|
+
{
|
|
12573
|
+
className: "uf-h-10 hover:uf-opacity-90 uf-text-foreground disabled:uf-opacity-50",
|
|
12574
|
+
style: {
|
|
12575
|
+
backgroundColor: components.card.backgroundColor,
|
|
12576
|
+
border: `${components.card.borderWidth}px solid ${components.card.borderColor}`
|
|
12577
|
+
},
|
|
12578
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(SelectValue, { children: isLoading || !selectedChainKey ? /* @__PURE__ */ (0, import_jsx_runtime52.jsx)("span", { className: "uf-text-xs uf-font-light uf-text-muted-foreground", children: t7.loading }) : currentChainData ? renderChainItem(currentChainData) : /* @__PURE__ */ (0, import_jsx_runtime52.jsx)("span", { className: "uf-text-xs uf-font-normal", children: selectedChainKey }) })
|
|
12579
|
+
}
|
|
12580
|
+
),
|
|
12581
|
+
/* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
|
|
12582
|
+
SelectContent,
|
|
12583
|
+
{
|
|
12584
|
+
align: "end",
|
|
12585
|
+
className: "uf-bg-secondary uf-border uf-text-foreground uf-max-h-[300px] uf-min-w-[200px]",
|
|
12586
|
+
style: {
|
|
12587
|
+
border: `1px solid ${isDarkMode ? "rgba(255,255,255,0.15)" : "rgba(0,0,0,0.15)"}`,
|
|
12588
|
+
...fonts.regular ? { "--uf-font-family": fonts.regular } : {}
|
|
12589
|
+
},
|
|
12590
|
+
children: availableChainsForToken.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime52.jsx)("div", { className: "uf-px-2 uf-py-3 uf-text-xs uf-text-muted-foreground uf-text-center", children: "No chains available" }) : availableChainsForToken.map((chainData) => {
|
|
12591
|
+
const chainKey = getChainKey4(chainData.chain_id, chainData.chain_type);
|
|
12592
|
+
return /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
|
|
12593
|
+
SelectItem,
|
|
12594
|
+
{
|
|
12595
|
+
value: chainKey,
|
|
12596
|
+
className: "focus:uf-bg-accent focus:uf-text-foreground",
|
|
12597
|
+
children: renderChainItem(chainData)
|
|
12598
|
+
},
|
|
12599
|
+
chainKey
|
|
12600
|
+
);
|
|
12601
|
+
})
|
|
12602
|
+
}
|
|
12603
|
+
)
|
|
12604
|
+
]
|
|
12605
|
+
}
|
|
12606
|
+
)
|
|
12607
|
+
] })
|
|
12608
|
+
] });
|
|
12609
|
+
}
|
|
12610
|
+
|
|
12611
|
+
// src/components/withdrawals/WithdrawForm.tsx
|
|
12612
|
+
var import_react20 = require("react");
|
|
12613
|
+
var import_lucide_react24 = require("lucide-react");
|
|
12614
|
+
|
|
12615
|
+
// src/hooks/use-verify-recipient-address.ts
|
|
12616
|
+
var import_react_query13 = require("@tanstack/react-query");
|
|
12617
|
+
var import_core29 = require("@unifold/core");
|
|
12618
|
+
function useVerifyRecipientAddress(params) {
|
|
12619
|
+
const {
|
|
12620
|
+
chainType,
|
|
12621
|
+
chainId,
|
|
12622
|
+
tokenAddress,
|
|
12623
|
+
recipientAddress,
|
|
12624
|
+
publishableKey,
|
|
12625
|
+
enabled = true
|
|
12626
|
+
} = params;
|
|
12627
|
+
const trimmedAddress = recipientAddress?.trim() || "";
|
|
12628
|
+
const hasAllParams = !!chainType && !!chainId && !!tokenAddress && trimmedAddress.length > 0;
|
|
12629
|
+
return (0, import_react_query13.useQuery)({
|
|
12630
|
+
queryKey: [
|
|
12631
|
+
"unifold",
|
|
12632
|
+
"verifyRecipientAddress",
|
|
12633
|
+
chainType ?? null,
|
|
12634
|
+
chainId ?? null,
|
|
12635
|
+
tokenAddress ?? null,
|
|
12636
|
+
trimmedAddress,
|
|
12637
|
+
publishableKey
|
|
12638
|
+
],
|
|
12639
|
+
queryFn: () => (0, import_core29.verifyRecipientAddress)(
|
|
12640
|
+
{
|
|
12641
|
+
chain_type: chainType,
|
|
12642
|
+
chain_id: chainId,
|
|
12643
|
+
token_address: tokenAddress,
|
|
12644
|
+
recipient_address: trimmedAddress
|
|
12645
|
+
},
|
|
12646
|
+
publishableKey
|
|
12647
|
+
),
|
|
12648
|
+
enabled: enabled && hasAllParams,
|
|
12649
|
+
staleTime: 1e3 * 60 * 5,
|
|
12650
|
+
gcTime: 1e3 * 60 * 30,
|
|
12651
|
+
retry: 1,
|
|
12652
|
+
refetchOnMount: false,
|
|
12653
|
+
refetchOnWindowFocus: false
|
|
12654
|
+
});
|
|
12655
|
+
}
|
|
12656
|
+
|
|
12657
|
+
// src/components/withdrawals/send-withdraw.ts
|
|
12658
|
+
var import_core30 = require("@unifold/core");
|
|
12659
|
+
async function sendEvmWithdraw(params) {
|
|
12660
|
+
const {
|
|
12661
|
+
provider,
|
|
12662
|
+
fromAddress,
|
|
12663
|
+
depositWalletAddress,
|
|
12664
|
+
sourceTokenAddress,
|
|
12665
|
+
sourceChainId,
|
|
12666
|
+
amountBaseUnit
|
|
12667
|
+
} = params;
|
|
12668
|
+
const currentChainIdHex = await provider.request({
|
|
12669
|
+
method: "eth_chainId",
|
|
12670
|
+
params: []
|
|
12671
|
+
});
|
|
12672
|
+
const currentChainId = parseInt(currentChainIdHex, 16).toString();
|
|
12673
|
+
if (currentChainId !== sourceChainId) {
|
|
12674
|
+
const requiredHex = "0x" + parseInt(sourceChainId).toString(16);
|
|
12675
|
+
try {
|
|
12676
|
+
await provider.request({
|
|
12677
|
+
method: "wallet_switchEthereumChain",
|
|
12678
|
+
params: [{ chainId: requiredHex }]
|
|
12679
|
+
});
|
|
12680
|
+
const newHex = await provider.request({ method: "eth_chainId", params: [] });
|
|
12681
|
+
if (parseInt(newHex, 16).toString() !== sourceChainId) {
|
|
12682
|
+
throw new Error(`Failed to switch to chain ${sourceChainId}. Please switch manually.`);
|
|
12683
|
+
}
|
|
12684
|
+
} catch (err) {
|
|
12685
|
+
if (err && typeof err === "object" && "code" in err) {
|
|
12686
|
+
const e = err;
|
|
12687
|
+
if (e.code === 4902) throw new Error(`Chain ${sourceChainId} is not configured in your wallet.`);
|
|
12688
|
+
if (e.code === 4001) throw new Error("You must approve the network switch to withdraw.");
|
|
12689
|
+
}
|
|
12690
|
+
throw err;
|
|
12691
|
+
}
|
|
12692
|
+
}
|
|
12693
|
+
const isNative = sourceTokenAddress === "native" || sourceTokenAddress === "0x0000000000000000000000000000000000000000" || sourceTokenAddress === "";
|
|
12694
|
+
const amountBig = BigInt(amountBaseUnit);
|
|
12695
|
+
const txParams = isNative ? { from: fromAddress, to: depositWalletAddress, value: "0x" + amountBig.toString(16) } : {
|
|
12696
|
+
from: fromAddress,
|
|
12697
|
+
to: sourceTokenAddress,
|
|
12698
|
+
data: "0xa9059cbb" + depositWalletAddress.slice(2).padStart(64, "0") + amountBig.toString(16).padStart(64, "0")
|
|
12699
|
+
};
|
|
12700
|
+
let gasEstimate;
|
|
12701
|
+
try {
|
|
12702
|
+
const hex = await provider.request({ method: "eth_estimateGas", params: [txParams] });
|
|
12703
|
+
gasEstimate = BigInt(hex);
|
|
12704
|
+
} catch {
|
|
12705
|
+
gasEstimate = isNative ? BigInt(21e3) : BigInt(65e3);
|
|
12706
|
+
}
|
|
12707
|
+
const gasPrice = BigInt(await provider.request({ method: "eth_gasPrice", params: [] }));
|
|
12708
|
+
const gasWithBuffer = gasEstimate * BigInt(120) / BigInt(100);
|
|
12709
|
+
const gasCost = gasWithBuffer * gasPrice;
|
|
12710
|
+
const ethBalance = BigInt(
|
|
12711
|
+
await provider.request({ method: "eth_getBalance", params: [fromAddress, "latest"] })
|
|
12712
|
+
);
|
|
12713
|
+
const totalRequired = isNative ? gasCost + amountBig : gasCost;
|
|
12714
|
+
if (ethBalance < totalRequired) {
|
|
12715
|
+
const gasFmt = (Number(gasCost) / 1e18).toFixed(6);
|
|
12716
|
+
if (isNative) {
|
|
12717
|
+
throw new Error(`Insufficient balance. Need ${(Number(totalRequired) / 1e18).toFixed(6)} ETH (amount + ~${gasFmt} gas).`);
|
|
12718
|
+
}
|
|
12719
|
+
throw new Error(`Insufficient ETH for gas. Need ~${gasFmt} ETH for fees.`);
|
|
12720
|
+
}
|
|
12721
|
+
const txHash = await provider.request({ method: "eth_sendTransaction", params: [txParams] });
|
|
12722
|
+
return txHash;
|
|
12723
|
+
}
|
|
12724
|
+
async function sendSolanaWithdraw(params) {
|
|
12725
|
+
const {
|
|
12726
|
+
provider,
|
|
12727
|
+
fromAddress,
|
|
12728
|
+
depositWalletAddress,
|
|
12729
|
+
sourceTokenAddress,
|
|
12730
|
+
amountBaseUnit,
|
|
12731
|
+
publishableKey
|
|
12732
|
+
} = params;
|
|
12733
|
+
if (!provider.publicKey) {
|
|
12734
|
+
await provider.connect();
|
|
12735
|
+
}
|
|
12736
|
+
const buildResponse = await (0, import_core30.buildSolanaTransaction)(
|
|
12737
|
+
{
|
|
12738
|
+
chain_id: "mainnet",
|
|
12739
|
+
token_address: sourceTokenAddress === "" ? "native" : sourceTokenAddress,
|
|
12740
|
+
source_address: fromAddress,
|
|
12741
|
+
destination_address: depositWalletAddress,
|
|
12742
|
+
amount: amountBaseUnit
|
|
12743
|
+
},
|
|
12744
|
+
publishableKey
|
|
12745
|
+
);
|
|
12746
|
+
const { VersionedTransaction } = await import(
|
|
12747
|
+
/* @vite-ignore */
|
|
12748
|
+
"@solana/web3.js"
|
|
12749
|
+
);
|
|
12750
|
+
const binaryString = atob(buildResponse.transaction);
|
|
12751
|
+
const bytes = new Uint8Array(binaryString.length);
|
|
12752
|
+
for (let i = 0; i < binaryString.length; i++) {
|
|
12753
|
+
bytes[i] = binaryString.charCodeAt(i);
|
|
12754
|
+
}
|
|
12755
|
+
const transaction = VersionedTransaction.deserialize(bytes);
|
|
12756
|
+
const signedTransaction = await provider.signTransaction(transaction);
|
|
12757
|
+
const serialized = signedTransaction.serialize();
|
|
12758
|
+
let binaryStr = "";
|
|
12759
|
+
for (let i = 0; i < serialized.length; i++) {
|
|
12760
|
+
binaryStr += String.fromCharCode(serialized[i]);
|
|
12761
|
+
}
|
|
12762
|
+
const sendResponse = await (0, import_core30.sendSolanaTransaction)(
|
|
12763
|
+
{ chain_id: "mainnet", signed_transaction: btoa(binaryStr) },
|
|
12764
|
+
publishableKey
|
|
12765
|
+
);
|
|
12766
|
+
return sendResponse.signature;
|
|
12767
|
+
}
|
|
12768
|
+
async function detectBrowserWallet(chainType, senderAddress) {
|
|
12769
|
+
const win = typeof window !== "undefined" ? window : null;
|
|
12770
|
+
if (!win || !senderAddress) return null;
|
|
12771
|
+
const anyWin = win;
|
|
12772
|
+
if (chainType === "solana") {
|
|
12773
|
+
const solProviders = [];
|
|
12774
|
+
if (win.phantom?.solana) solProviders.push({ provider: win.phantom.solana, name: "Phantom" });
|
|
12775
|
+
if (anyWin.solflare) solProviders.push({ provider: anyWin.solflare, name: "Solflare" });
|
|
12776
|
+
if (anyWin.backpack) solProviders.push({ provider: anyWin.backpack, name: "Backpack" });
|
|
12777
|
+
if (anyWin.trustwallet?.solana) solProviders.push({ provider: anyWin.trustwallet.solana, name: "Trust Wallet" });
|
|
12778
|
+
for (const { provider, name } of solProviders) {
|
|
12779
|
+
if (!provider) continue;
|
|
12780
|
+
try {
|
|
12781
|
+
let addr;
|
|
12782
|
+
if (provider.isConnected && provider.publicKey) {
|
|
12783
|
+
addr = provider.publicKey.toString();
|
|
12784
|
+
} else {
|
|
12785
|
+
const resp = await provider.connect({ onlyIfTrusted: true });
|
|
12786
|
+
if (resp?.publicKey) addr = resp.publicKey.toString();
|
|
12787
|
+
}
|
|
12788
|
+
if (addr && addr === senderAddress) {
|
|
12789
|
+
return { chainFamily: "solana", provider, name, address: addr };
|
|
12790
|
+
}
|
|
12791
|
+
} catch {
|
|
12792
|
+
}
|
|
12793
|
+
}
|
|
12794
|
+
}
|
|
12795
|
+
if (chainType === "ethereum") {
|
|
12796
|
+
const evmProviders = [];
|
|
12797
|
+
const seen = /* @__PURE__ */ new Set();
|
|
12798
|
+
const add = (p, name) => {
|
|
12799
|
+
if (p && typeof p.request === "function" && !seen.has(p)) {
|
|
12800
|
+
seen.add(p);
|
|
12801
|
+
evmProviders.push({ provider: p, name });
|
|
12802
|
+
}
|
|
12803
|
+
};
|
|
12804
|
+
add(anyWin.phantom?.ethereum, "Phantom");
|
|
12805
|
+
add(anyWin.coinbaseWalletExtension, "Coinbase");
|
|
12806
|
+
add(anyWin.trustwallet?.ethereum, "Trust Wallet");
|
|
12807
|
+
add(anyWin.okxwallet, "OKX Wallet");
|
|
12808
|
+
if (anyWin.__eip6963Providers) {
|
|
12809
|
+
for (const detail of anyWin.__eip6963Providers) {
|
|
12810
|
+
const rdns = detail.info?.rdns || "";
|
|
12811
|
+
let name = detail.info?.name || "Wallet";
|
|
12812
|
+
if (rdns.includes("metamask")) name = "MetaMask";
|
|
12813
|
+
else if (rdns.includes("rabby")) name = "Rabby";
|
|
12814
|
+
else if (rdns.includes("rainbow")) name = "Rainbow";
|
|
12815
|
+
add(detail.provider, name);
|
|
12816
|
+
}
|
|
12817
|
+
}
|
|
12818
|
+
if (win.ethereum) {
|
|
12819
|
+
const eth = win.ethereum;
|
|
12820
|
+
let name = "Wallet";
|
|
12821
|
+
if (eth.isMetaMask && !eth.isPhantom && !eth.isRabby) name = "MetaMask";
|
|
12822
|
+
else if (eth.isRabby) name = "Rabby";
|
|
12823
|
+
else if (eth.isRainbow) name = "Rainbow";
|
|
12824
|
+
else if (eth.isCoinbaseWallet) name = "Coinbase";
|
|
12825
|
+
add(eth, name);
|
|
12826
|
+
}
|
|
12827
|
+
for (const { provider, name } of evmProviders) {
|
|
12828
|
+
try {
|
|
12829
|
+
const accounts = await provider.request({ method: "eth_accounts" });
|
|
12830
|
+
if (accounts?.length > 0 && accounts[0].toLowerCase() === senderAddress.toLowerCase()) {
|
|
12831
|
+
return { chainFamily: "evm", provider, name, address: accounts[0] };
|
|
12832
|
+
}
|
|
12833
|
+
} catch {
|
|
12834
|
+
}
|
|
12835
|
+
}
|
|
12836
|
+
}
|
|
12837
|
+
return null;
|
|
12838
|
+
}
|
|
12839
|
+
|
|
12840
|
+
// src/components/withdrawals/WithdrawForm.tsx
|
|
12841
|
+
var import_jsx_runtime53 = require("react/jsx-runtime");
|
|
12842
|
+
var t8 = i18n.withdrawModal;
|
|
12843
|
+
var tCrypto = i18n.transferCrypto;
|
|
12844
|
+
function formatProcessingTime2(seconds) {
|
|
12845
|
+
if (seconds === null) {
|
|
12846
|
+
return tCrypto.processingTime.lessThanMinutes.replace("{{minutes}}", "1");
|
|
12847
|
+
}
|
|
12848
|
+
const minutes = Math.ceil(seconds / 60);
|
|
12849
|
+
if (minutes < 60) {
|
|
12850
|
+
return tCrypto.processingTime.lessThanMinutes.replace("{{minutes}}", String(minutes));
|
|
12851
|
+
}
|
|
12852
|
+
const hours = Math.ceil(minutes / 60);
|
|
12853
|
+
return tCrypto.processingTime.lessThanHours.replace("{{hours}}", String(hours));
|
|
12854
|
+
}
|
|
12855
|
+
function computeBaseUnit(balanceBaseUnit, inputAmount, balanceAmount) {
|
|
12856
|
+
if (balanceAmount <= 0 || inputAmount <= 0) return "0";
|
|
12857
|
+
if (inputAmount >= balanceAmount) return balanceBaseUnit;
|
|
12858
|
+
const PRECISION = 10n ** 18n;
|
|
12859
|
+
const ratioScaled = BigInt(Math.round(inputAmount / balanceAmount * Number(PRECISION)));
|
|
12860
|
+
const result = BigInt(balanceBaseUnit) * ratioScaled / PRECISION;
|
|
12861
|
+
return result.toString();
|
|
12862
|
+
}
|
|
12863
|
+
function toSafeDecimalString(n, maxDecimals) {
|
|
12864
|
+
if (n === 0) return "0";
|
|
12865
|
+
return n.toFixed(maxDecimals).replace(/\.?0+$/, "");
|
|
12866
|
+
}
|
|
12867
|
+
function WithdrawForm({
|
|
12868
|
+
publishableKey,
|
|
12869
|
+
externalUserId,
|
|
12870
|
+
sourceChainType,
|
|
12871
|
+
selectedToken,
|
|
12872
|
+
selectedChain,
|
|
12873
|
+
sourceTokenSymbol,
|
|
12874
|
+
recipientAddressProp,
|
|
12875
|
+
balanceData,
|
|
12876
|
+
isLoadingBalance,
|
|
12877
|
+
minimumWithdrawAmountUsd,
|
|
12878
|
+
estimatedProcessingTime,
|
|
12879
|
+
maxSlippagePercent,
|
|
12880
|
+
priceImpactPercent,
|
|
12881
|
+
detectedWallet,
|
|
12882
|
+
sourceChainId,
|
|
12883
|
+
sourceTokenAddress,
|
|
12884
|
+
isWalletMatch,
|
|
12885
|
+
connectedWalletName,
|
|
12886
|
+
canWithdraw,
|
|
12887
|
+
onWithdraw,
|
|
12888
|
+
onWithdrawError,
|
|
12889
|
+
onDepositWalletCreation,
|
|
12890
|
+
onWithdrawSubmitted,
|
|
12891
|
+
footerLeft
|
|
12892
|
+
}) {
|
|
12893
|
+
const { colors: colors2, fonts, components } = useTheme();
|
|
12894
|
+
const [recipientAddress, setRecipientAddress] = (0, import_react20.useState)(recipientAddressProp || "");
|
|
12895
|
+
const [amount, setAmount] = (0, import_react20.useState)("");
|
|
12896
|
+
const [inputUnit, setInputUnit] = (0, import_react20.useState)("crypto");
|
|
12897
|
+
const [isSubmitting, setIsSubmitting] = (0, import_react20.useState)(false);
|
|
12898
|
+
const [submitError, setSubmitError] = (0, import_react20.useState)(null);
|
|
12899
|
+
const [detailsExpanded, setDetailsExpanded] = (0, import_react20.useState)(false);
|
|
12900
|
+
const [glossaryOpen, setGlossaryOpen] = (0, import_react20.useState)(false);
|
|
12901
|
+
(0, import_react20.useEffect)(() => {
|
|
12902
|
+
setRecipientAddress(recipientAddressProp || "");
|
|
12903
|
+
setAmount("");
|
|
12904
|
+
setInputUnit("crypto");
|
|
12905
|
+
setSubmitError(null);
|
|
12906
|
+
}, [recipientAddressProp]);
|
|
12907
|
+
const trimmedAddress = recipientAddress.trim();
|
|
12908
|
+
const [debouncedAddress, setDebouncedAddress] = (0, import_react20.useState)(trimmedAddress);
|
|
12909
|
+
(0, import_react20.useEffect)(() => {
|
|
12910
|
+
const id = setTimeout(() => setDebouncedAddress(trimmedAddress), 500);
|
|
12911
|
+
return () => clearTimeout(id);
|
|
12912
|
+
}, [trimmedAddress]);
|
|
12913
|
+
const {
|
|
12914
|
+
data: addressVerification,
|
|
12915
|
+
isLoading: isVerifyingAddress,
|
|
12916
|
+
error: verifyError
|
|
12917
|
+
} = useVerifyRecipientAddress({
|
|
12918
|
+
chainType: selectedChain?.chain_type,
|
|
12919
|
+
chainId: selectedChain?.chain_id,
|
|
12920
|
+
tokenAddress: selectedChain?.token_address,
|
|
12921
|
+
recipientAddress: debouncedAddress,
|
|
12922
|
+
publishableKey,
|
|
12923
|
+
enabled: debouncedAddress.length > 5 && !!selectedChain
|
|
12924
|
+
});
|
|
12925
|
+
const isDebouncing = trimmedAddress !== debouncedAddress;
|
|
12926
|
+
const addressError = (0, import_react20.useMemo)(() => {
|
|
12927
|
+
if (!trimmedAddress || trimmedAddress.length <= 5) return null;
|
|
12928
|
+
if (isDebouncing || isVerifyingAddress) return null;
|
|
12929
|
+
if (verifyError) return t8.invalidAddress;
|
|
12930
|
+
if (addressVerification && !addressVerification.valid) {
|
|
12931
|
+
if (addressVerification.failure_code === "account_not_found")
|
|
12932
|
+
return `Account not found on ${selectedChain?.chain_name}`;
|
|
12933
|
+
if (addressVerification.failure_code === "not_opted_in")
|
|
12934
|
+
return `Recipient has not opted in to ${selectedToken?.symbol} on ${selectedChain?.chain_name}`;
|
|
12935
|
+
return t8.invalidAddress;
|
|
12936
|
+
}
|
|
12937
|
+
return null;
|
|
12938
|
+
}, [trimmedAddress, isDebouncing, isVerifyingAddress, verifyError, addressVerification, selectedChain, selectedToken]);
|
|
12939
|
+
const isAddressValid = !isDebouncing && !!addressVerification?.valid && !addressError;
|
|
12940
|
+
const exchangeRate = (0, import_react20.useMemo)(() => {
|
|
12941
|
+
if (!balanceData?.exchangeRate) return 0;
|
|
12942
|
+
return parseFloat(balanceData.exchangeRate);
|
|
12943
|
+
}, [balanceData]);
|
|
12944
|
+
const balanceCrypto = (0, import_react20.useMemo)(() => {
|
|
12945
|
+
if (!balanceData?.balanceHuman) return 0;
|
|
12946
|
+
return parseFloat(balanceData.balanceHuman);
|
|
12947
|
+
}, [balanceData]);
|
|
12948
|
+
const balanceUsdNum = (0, import_react20.useMemo)(() => {
|
|
12949
|
+
if (!balanceData?.balanceUsd) return 0;
|
|
12950
|
+
return parseFloat(balanceData.balanceUsd);
|
|
12951
|
+
}, [balanceData]);
|
|
12952
|
+
const tokenSymbol = sourceTokenSymbol || balanceData?.symbol || "TOKEN";
|
|
12953
|
+
const sourceDecimals = balanceData?.decimals ?? 6;
|
|
12954
|
+
const cryptoAmountFromInput = (0, import_react20.useMemo)(() => {
|
|
12955
|
+
const val = parseFloat(amount);
|
|
12956
|
+
if (!val || val <= 0) return 0;
|
|
12957
|
+
if (inputUnit === "crypto") return val;
|
|
12958
|
+
return exchangeRate > 0 ? val / exchangeRate : 0;
|
|
12959
|
+
}, [amount, inputUnit, exchangeRate]);
|
|
12960
|
+
const fiatAmountFromInput = (0, import_react20.useMemo)(() => {
|
|
12961
|
+
const val = parseFloat(amount);
|
|
12962
|
+
if (!val || val <= 0) return 0;
|
|
12963
|
+
if (inputUnit === "fiat") return val;
|
|
12964
|
+
return val * exchangeRate;
|
|
12965
|
+
}, [amount, inputUnit, exchangeRate]);
|
|
12966
|
+
const convertedDisplay = (0, import_react20.useMemo)(() => {
|
|
12967
|
+
if (!amount || parseFloat(amount) <= 0) return null;
|
|
12968
|
+
if (inputUnit === "crypto") {
|
|
12969
|
+
return `$${fiatAmountFromInput.toLocaleString(void 0, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`;
|
|
12970
|
+
}
|
|
12971
|
+
return `${cryptoAmountFromInput.toLocaleString(void 0, { minimumFractionDigits: 2, maximumFractionDigits: 6 })} ${tokenSymbol}`;
|
|
12972
|
+
}, [amount, inputUnit, fiatAmountFromInput, cryptoAmountFromInput, tokenSymbol]);
|
|
12973
|
+
const balanceDisplay = (0, import_react20.useMemo)(() => {
|
|
12974
|
+
if (isLoadingBalance || !balanceData) return null;
|
|
12975
|
+
if (inputUnit === "crypto") {
|
|
12976
|
+
return `${balanceCrypto.toLocaleString(void 0, { minimumFractionDigits: 2, maximumFractionDigits: 2 })} ${tokenSymbol}`;
|
|
12977
|
+
}
|
|
12978
|
+
return `$${balanceUsdNum.toLocaleString(void 0, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`;
|
|
12979
|
+
}, [isLoadingBalance, balanceData, inputUnit, balanceCrypto, balanceUsdNum, tokenSymbol]);
|
|
12980
|
+
const handleSwitchUnit = (0, import_react20.useCallback)(() => {
|
|
12981
|
+
const val = parseFloat(amount);
|
|
12982
|
+
if (!val || val <= 0 || exchangeRate <= 0) {
|
|
12983
|
+
setInputUnit((u) => u === "crypto" ? "fiat" : "crypto");
|
|
12984
|
+
setAmount("");
|
|
12985
|
+
return;
|
|
12986
|
+
}
|
|
12987
|
+
if (inputUnit === "crypto") {
|
|
12988
|
+
const fiat = val * exchangeRate;
|
|
12989
|
+
setAmount(fiat.toFixed(2));
|
|
12990
|
+
setInputUnit("fiat");
|
|
12991
|
+
} else {
|
|
12992
|
+
const crypto = val / exchangeRate;
|
|
12993
|
+
setAmount(crypto.toFixed(sourceDecimals > 6 ? 6 : sourceDecimals));
|
|
12994
|
+
setInputUnit("crypto");
|
|
12995
|
+
}
|
|
12996
|
+
}, [amount, inputUnit, exchangeRate, sourceDecimals]);
|
|
12997
|
+
const handleMaxClick = (0, import_react20.useCallback)(() => {
|
|
12998
|
+
if (inputUnit === "crypto") {
|
|
12999
|
+
if (balanceCrypto <= 0) return;
|
|
13000
|
+
setAmount(balanceData?.balanceHuman ?? "0");
|
|
13001
|
+
} else {
|
|
13002
|
+
if (balanceUsdNum <= 0) return;
|
|
13003
|
+
setAmount((Math.floor(balanceUsdNum * 100) / 100).toFixed(2));
|
|
13004
|
+
}
|
|
13005
|
+
}, [inputUnit, balanceCrypto, balanceUsdNum, balanceData]);
|
|
13006
|
+
const isBelowMinimum = minimumWithdrawAmountUsd !== null && fiatAmountFromInput > 0 && fiatAmountFromInput < minimumWithdrawAmountUsd;
|
|
13007
|
+
const isOverBalance = inputUnit === "crypto" ? cryptoAmountFromInput > 0 && balanceCrypto > 0 && cryptoAmountFromInput > balanceCrypto : fiatAmountFromInput > 0 && balanceUsdNum > 0 && fiatAmountFromInput > balanceUsdNum;
|
|
13008
|
+
const isFormValid = trimmedAddress.length > 0 && amount.trim().length > 0 && cryptoAmountFromInput > 0 && isAddressValid && !isBelowMinimum && !isOverBalance && !!balanceData;
|
|
13009
|
+
const handleWithdraw = (0, import_react20.useCallback)(async () => {
|
|
13010
|
+
if (!selectedToken || !selectedChain) return;
|
|
13011
|
+
if (!isFormValid) return;
|
|
13012
|
+
setIsSubmitting(true);
|
|
13013
|
+
setSubmitError(null);
|
|
13014
|
+
try {
|
|
13015
|
+
const depositWallet = await onDepositWalletCreation({
|
|
13016
|
+
destinationChainType: selectedChain.chain_type,
|
|
13017
|
+
destinationChainId: selectedChain.chain_id,
|
|
13018
|
+
destinationTokenAddress: selectedChain.token_address,
|
|
13019
|
+
recipientAddress: trimmedAddress
|
|
13020
|
+
});
|
|
13021
|
+
const amountBaseUnit = computeBaseUnit(
|
|
13022
|
+
balanceData.balanceBaseUnit,
|
|
13023
|
+
parseFloat(amount),
|
|
13024
|
+
inputUnit === "crypto" ? balanceCrypto : balanceUsdNum
|
|
13025
|
+
);
|
|
13026
|
+
const humanAmount = toSafeDecimalString(cryptoAmountFromInput, sourceDecimals);
|
|
13027
|
+
const txInfo = {
|
|
13028
|
+
sourceChainType,
|
|
13029
|
+
sourceChainId,
|
|
13030
|
+
sourceTokenAddress,
|
|
13031
|
+
sourceTokenSymbol: tokenSymbol,
|
|
13032
|
+
destinationChainType: selectedChain.chain_type,
|
|
13033
|
+
destinationChainId: selectedChain.chain_id,
|
|
13034
|
+
destinationTokenAddress: selectedChain.token_address,
|
|
13035
|
+
destinationTokenSymbol: selectedToken.symbol,
|
|
13036
|
+
amount: humanAmount,
|
|
13037
|
+
amountBaseUnit,
|
|
13038
|
+
withdrawIntentAddress: depositWallet.address,
|
|
13039
|
+
recipientAddress: trimmedAddress
|
|
13040
|
+
};
|
|
13041
|
+
if (detectedWallet) {
|
|
13042
|
+
if (detectedWallet.chainFamily === "evm") {
|
|
13043
|
+
await sendEvmWithdraw({
|
|
13044
|
+
provider: detectedWallet.provider,
|
|
13045
|
+
fromAddress: detectedWallet.address,
|
|
13046
|
+
depositWalletAddress: depositWallet.address,
|
|
13047
|
+
sourceTokenAddress,
|
|
13048
|
+
sourceChainId,
|
|
13049
|
+
amountBaseUnit
|
|
13050
|
+
});
|
|
13051
|
+
} else if (detectedWallet.chainFamily === "solana") {
|
|
13052
|
+
await sendSolanaWithdraw({
|
|
13053
|
+
provider: detectedWallet.provider,
|
|
13054
|
+
fromAddress: detectedWallet.address,
|
|
13055
|
+
depositWalletAddress: depositWallet.address,
|
|
13056
|
+
sourceTokenAddress,
|
|
13057
|
+
amountBaseUnit,
|
|
13058
|
+
publishableKey
|
|
13059
|
+
});
|
|
13060
|
+
}
|
|
13061
|
+
} else if (onWithdraw) {
|
|
13062
|
+
await onWithdraw(txInfo);
|
|
13063
|
+
} else {
|
|
13064
|
+
throw new Error("No withdrawal method available. Please connect a wallet.");
|
|
13065
|
+
}
|
|
13066
|
+
onWithdrawSubmitted?.(txInfo);
|
|
13067
|
+
} catch (err) {
|
|
13068
|
+
const raw = err instanceof Error ? err.message : "Withdrawal failed. Please try again.";
|
|
13069
|
+
setSubmitError(raw.length > 120 ? "Withdrawal failed. Please try again." : raw);
|
|
13070
|
+
onWithdrawError?.({
|
|
13071
|
+
message: raw,
|
|
13072
|
+
error: err,
|
|
13073
|
+
code: "WITHDRAW_FAILED"
|
|
13074
|
+
});
|
|
13075
|
+
} finally {
|
|
13076
|
+
setIsSubmitting(false);
|
|
13077
|
+
}
|
|
13078
|
+
}, [selectedToken, selectedChain, isFormValid, cryptoAmountFromInput, sourceDecimals, trimmedAddress, publishableKey, onWithdraw, detectedWallet, sourceTokenAddress, sourceChainId, onWithdrawError, onDepositWalletCreation, onWithdrawSubmitted, amount, inputUnit, balanceCrypto, balanceUsdNum, balanceData]);
|
|
13079
|
+
return /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)(import_jsx_runtime53.Fragment, { children: [
|
|
13080
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsxs)("div", { children: [
|
|
13081
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
|
|
13082
|
+
"div",
|
|
13083
|
+
{
|
|
13084
|
+
className: "uf-text-xs uf-mb-1.5",
|
|
13085
|
+
style: { color: components.card.labelColor, fontFamily: fonts.medium },
|
|
13086
|
+
children: t8.recipientAddress
|
|
13087
|
+
}
|
|
13088
|
+
),
|
|
13089
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
|
|
13090
|
+
"style",
|
|
13091
|
+
{
|
|
13092
|
+
dangerouslySetInnerHTML: {
|
|
13093
|
+
__html: `.uf-withdraw-addr::placeholder { color: ${components.search.placeholderColor}; }`
|
|
13094
|
+
}
|
|
13095
|
+
}
|
|
13096
|
+
),
|
|
13097
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsxs)(
|
|
13098
|
+
"div",
|
|
13099
|
+
{
|
|
13100
|
+
className: "uf-flex uf-items-center uf-gap-1 uf-pr-2",
|
|
13101
|
+
style: {
|
|
13102
|
+
backgroundColor: components.search.backgroundColor,
|
|
13103
|
+
borderRadius: components.input.borderRadius,
|
|
13104
|
+
border: `${components.input.borderWidth}px solid ${addressError ? colors2.error : components.input.borderColor}`
|
|
13105
|
+
},
|
|
13106
|
+
children: [
|
|
13107
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
|
|
13108
|
+
"input",
|
|
13109
|
+
{
|
|
13110
|
+
type: "text",
|
|
13111
|
+
placeholder: t8.recipientAddressPlaceholder,
|
|
13112
|
+
value: recipientAddress,
|
|
13113
|
+
onChange: (e) => {
|
|
13114
|
+
setRecipientAddress(e.target.value);
|
|
13115
|
+
setSubmitError(null);
|
|
13116
|
+
},
|
|
13117
|
+
className: "uf-withdraw-addr uf-flex-1 uf-min-w-0 uf-px-3 uf-py-2.5 uf-text-sm uf-bg-transparent uf-outline-none",
|
|
13118
|
+
style: {
|
|
13119
|
+
color: components.search.inputColor,
|
|
13120
|
+
fontFamily: fonts.regular
|
|
13121
|
+
}
|
|
13122
|
+
}
|
|
13123
|
+
),
|
|
13124
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
|
|
13125
|
+
"button",
|
|
13126
|
+
{
|
|
13127
|
+
type: "button",
|
|
13128
|
+
onClick: async () => {
|
|
13129
|
+
try {
|
|
13130
|
+
const text = await navigator.clipboard.readText();
|
|
13131
|
+
if (text) {
|
|
13132
|
+
setRecipientAddress(text.trim());
|
|
13133
|
+
setSubmitError(null);
|
|
13134
|
+
}
|
|
13135
|
+
} catch {
|
|
13136
|
+
}
|
|
13137
|
+
},
|
|
13138
|
+
className: "uf-flex-shrink-0 uf-p-1 uf-rounded uf-transition-colors hover:uf-opacity-70",
|
|
13139
|
+
style: { color: colors2.foregroundMuted },
|
|
13140
|
+
title: "Paste from clipboard",
|
|
13141
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(import_lucide_react24.ClipboardPaste, { className: "uf-w-4 uf-h-4" })
|
|
13142
|
+
}
|
|
13143
|
+
)
|
|
13144
|
+
]
|
|
13145
|
+
}
|
|
13146
|
+
),
|
|
13147
|
+
(isDebouncing || isVerifyingAddress) && trimmedAddress.length > 5 && /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)("div", { className: "uf-flex uf-items-center uf-gap-1.5 uf-mt-1.5", children: [
|
|
13148
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)(import_lucide_react24.Loader2, { className: "uf-w-3 uf-h-3 uf-animate-spin", style: { color: colors2.foregroundMuted } }),
|
|
13149
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)("span", { className: "uf-text-xs", style: { color: colors2.foregroundMuted, fontFamily: fonts.regular }, children: t8.verifyingAddress })
|
|
13150
|
+
] }),
|
|
13151
|
+
addressError && /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)("div", { className: "uf-flex uf-items-center uf-gap-1.5 uf-mt-1.5", children: [
|
|
13152
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)(import_lucide_react24.AlertTriangle, { className: "uf-w-3 uf-h-3", style: { color: colors2.error } }),
|
|
13153
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)("span", { className: "uf-text-xs", style: { color: colors2.error, fontFamily: fonts.regular }, children: addressError })
|
|
13154
|
+
] })
|
|
13155
|
+
] }),
|
|
13156
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsxs)("div", { children: [
|
|
13157
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsxs)("div", { className: "uf-text-xs uf-mb-1.5", style: { color: components.card.labelColor, fontFamily: fonts.medium }, children: [
|
|
13158
|
+
t8.amount,
|
|
13159
|
+
minimumWithdrawAmountUsd != null && minimumWithdrawAmountUsd > 0 && /* @__PURE__ */ (0, import_jsx_runtime53.jsx)("span", { style: { color: colors2.warning, fontFamily: fonts.regular }, children: ` ($${minimumWithdrawAmountUsd.toFixed(2)} min)` })
|
|
13160
|
+
] }),
|
|
13161
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
|
|
13162
|
+
"style",
|
|
13163
|
+
{
|
|
13164
|
+
dangerouslySetInnerHTML: {
|
|
13165
|
+
__html: `.uf-withdraw-amt::placeholder { color: ${components.search.placeholderColor}; }`
|
|
13166
|
+
}
|
|
13167
|
+
}
|
|
13168
|
+
),
|
|
13169
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsxs)(
|
|
13170
|
+
"div",
|
|
13171
|
+
{
|
|
13172
|
+
className: "uf-flex uf-items-center uf-gap-2 uf-px-3 uf-py-2.5",
|
|
13173
|
+
style: {
|
|
13174
|
+
backgroundColor: components.search.backgroundColor,
|
|
13175
|
+
borderRadius: components.input.borderRadius,
|
|
13176
|
+
border: `${components.input.borderWidth}px solid ${components.input.borderColor}`
|
|
13177
|
+
},
|
|
13178
|
+
children: [
|
|
13179
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
|
|
13180
|
+
"input",
|
|
13181
|
+
{
|
|
13182
|
+
type: "text",
|
|
13183
|
+
inputMode: "decimal",
|
|
13184
|
+
placeholder: "0.00",
|
|
13185
|
+
value: amount,
|
|
13186
|
+
onChange: (e) => {
|
|
13187
|
+
const val = e.target.value;
|
|
13188
|
+
if (val === "" || /^\d*\.?\d*$/.test(val)) {
|
|
13189
|
+
setAmount(val);
|
|
13190
|
+
setSubmitError(null);
|
|
13191
|
+
}
|
|
13192
|
+
},
|
|
13193
|
+
className: "uf-withdraw-amt uf-flex-1 uf-min-w-0 uf-bg-transparent uf-text-sm uf-outline-none",
|
|
13194
|
+
style: {
|
|
13195
|
+
color: components.search.inputColor,
|
|
13196
|
+
fontFamily: fonts.regular
|
|
13197
|
+
}
|
|
13198
|
+
}
|
|
13199
|
+
),
|
|
13200
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)("span", { className: "uf-text-sm uf-shrink-0", style: { color: colors2.foregroundMuted, fontFamily: fonts.medium }, children: inputUnit === "crypto" ? tokenSymbol : "USD" }),
|
|
13201
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
|
|
13202
|
+
"button",
|
|
13203
|
+
{
|
|
13204
|
+
type: "button",
|
|
13205
|
+
onClick: handleMaxClick,
|
|
13206
|
+
className: "uf-shrink-0 uf-px-2 uf-py-0.5 uf-rounded-md uf-text-xs uf-font-medium uf-transition-colors hover:uf-opacity-80",
|
|
13207
|
+
style: { backgroundColor: colors2.primary + "20", color: colors2.primary, fontFamily: fonts.medium },
|
|
13208
|
+
children: "Max"
|
|
13209
|
+
}
|
|
13210
|
+
)
|
|
13211
|
+
]
|
|
13212
|
+
}
|
|
13213
|
+
),
|
|
13214
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsxs)("div", { className: "uf-flex uf-items-center uf-justify-between uf-mt-1.5 uf-px-3", children: [
|
|
13215
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsxs)("div", { className: "uf-flex uf-items-center uf-gap-1", children: [
|
|
13216
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)("span", { className: "uf-text-xs", style: { color: colors2.foregroundMuted, fontFamily: fonts.regular }, children: convertedDisplay || (inputUnit === "crypto" ? "$0.00" : `0.00 ${tokenSymbol}`) }),
|
|
13217
|
+
exchangeRate > 0 && /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
|
|
13218
|
+
"button",
|
|
13219
|
+
{
|
|
13220
|
+
type: "button",
|
|
13221
|
+
onClick: handleSwitchUnit,
|
|
13222
|
+
className: "uf-p-0.5 uf-rounded uf-transition-colors hover:uf-opacity-70",
|
|
13223
|
+
style: { color: colors2.foregroundMuted },
|
|
13224
|
+
title: "Switch unit",
|
|
13225
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(import_lucide_react24.ArrowUpDown, { className: "uf-w-3 uf-h-3" })
|
|
13226
|
+
}
|
|
13227
|
+
)
|
|
13228
|
+
] }),
|
|
13229
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsxs)("div", { children: [
|
|
13230
|
+
balanceDisplay && /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)("span", { className: "uf-text-xs", style: { color: colors2.foregroundMuted, fontFamily: fonts.regular }, children: [
|
|
13231
|
+
t8.balance,
|
|
13232
|
+
": ",
|
|
13233
|
+
balanceDisplay
|
|
13234
|
+
] }),
|
|
13235
|
+
isLoadingBalance && /* @__PURE__ */ (0, import_jsx_runtime53.jsx)("div", { className: "uf-h-3 uf-w-16 uf-bg-muted uf-rounded uf-animate-pulse" })
|
|
13236
|
+
] })
|
|
13237
|
+
] })
|
|
13238
|
+
] }),
|
|
13239
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsxs)("div", { className: "uf-px-2.5", style: { backgroundColor: components.card.backgroundColor, borderRadius: components.card.borderRadius, border: `${components.card.borderWidth}px solid ${components.card.borderColor}` }, children: [
|
|
13240
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsxs)(
|
|
13241
|
+
"button",
|
|
13242
|
+
{
|
|
13243
|
+
type: "button",
|
|
13244
|
+
onClick: () => setDetailsExpanded(!detailsExpanded),
|
|
13245
|
+
className: "uf-w-full uf-flex uf-items-center uf-justify-between uf-py-2.5",
|
|
13246
|
+
children: [
|
|
13247
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsxs)("div", { className: "uf-flex uf-items-center uf-gap-2", children: [
|
|
13248
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)("div", { className: "uf-rounded-full uf-p-1", style: { backgroundColor: components.card.iconBackgroundColor }, children: /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(import_lucide_react24.Clock, { className: "uf-w-3 uf-h-3", style: { color: components.card.iconColor } }) }),
|
|
13249
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsxs)("span", { className: "uf-text-xs", style: { color: components.card.labelColor, fontFamily: fonts.regular }, children: [
|
|
13250
|
+
tCrypto.processingTime.label,
|
|
13251
|
+
":",
|
|
13252
|
+
" ",
|
|
13253
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)("span", { style: { color: components.card.titleColor, fontFamily: fonts.medium }, children: formatProcessingTime2(estimatedProcessingTime) })
|
|
13254
|
+
] })
|
|
13255
|
+
] }),
|
|
13256
|
+
detailsExpanded ? /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(import_lucide_react24.ChevronUp, { className: "uf-w-4 uf-h-4", style: { color: components.card.actionColor } }) : /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(import_lucide_react24.ChevronDown, { className: "uf-w-4 uf-h-4", style: { color: components.card.actionColor } })
|
|
13257
|
+
]
|
|
13258
|
+
}
|
|
13259
|
+
),
|
|
13260
|
+
detailsExpanded && /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)("div", { className: "uf-pb-3 uf-space-y-2.5", children: [
|
|
13261
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsxs)("div", { className: "uf-flex uf-items-center uf-gap-2", children: [
|
|
13262
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)("div", { className: "uf-rounded-full uf-p-1", style: { backgroundColor: components.card.iconBackgroundColor }, children: /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(import_lucide_react24.ShieldCheck, { className: "uf-w-3 uf-h-3", style: { color: components.card.iconColor } }) }),
|
|
13263
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsxs)("span", { className: "uf-text-xs", style: { color: components.card.labelColor, fontFamily: fonts.regular }, children: [
|
|
13264
|
+
tCrypto.slippage.label,
|
|
13265
|
+
":",
|
|
13266
|
+
" ",
|
|
13267
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsxs)("span", { style: { color: components.card.titleColor, fontFamily: fonts.medium }, children: [
|
|
13268
|
+
tCrypto.slippage.auto,
|
|
13269
|
+
" \u2022 ",
|
|
13270
|
+
(maxSlippagePercent ?? 0.25).toFixed(2),
|
|
13271
|
+
"%"
|
|
13272
|
+
] })
|
|
13273
|
+
] })
|
|
13274
|
+
] }),
|
|
13275
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsxs)("div", { className: "uf-flex uf-items-center uf-gap-2", children: [
|
|
13276
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)("div", { className: "uf-rounded-full uf-p-1", style: { backgroundColor: components.card.iconBackgroundColor }, children: /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(import_lucide_react24.DollarSign, { className: "uf-w-3 uf-h-3", style: { color: components.card.iconColor } }) }),
|
|
13277
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsxs)("span", { className: "uf-text-xs", style: { color: components.card.labelColor, fontFamily: fonts.regular }, children: [
|
|
13278
|
+
tCrypto.priceImpact.label,
|
|
13279
|
+
":",
|
|
13280
|
+
" ",
|
|
13281
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsxs)("span", { style: { color: components.card.titleColor, fontFamily: fonts.medium }, children: [
|
|
13282
|
+
(priceImpactPercent ?? 0).toFixed(2),
|
|
13283
|
+
"%"
|
|
13284
|
+
] })
|
|
13285
|
+
] })
|
|
13286
|
+
] })
|
|
13287
|
+
] })
|
|
13288
|
+
] }),
|
|
13289
|
+
!canWithdraw && !submitError && /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)(
|
|
13290
|
+
"div",
|
|
13291
|
+
{
|
|
13292
|
+
className: "uf-flex uf-items-start uf-gap-2.5 uf-p-3 uf-rounded-xl",
|
|
13293
|
+
style: { backgroundColor: colors2.card, border: `1px solid ${colors2.border}` },
|
|
13294
|
+
children: [
|
|
13295
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)(import_lucide_react24.Wallet, { className: "uf-w-4 uf-h-4 uf-flex-shrink-0 uf-mt-0.5", style: { color: colors2.warning } }),
|
|
13296
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)("div", { className: "uf-text-xs", style: { color: colors2.foregroundMuted, fontFamily: fonts.regular }, children: "No connected wallet detected. Please connect a wallet that matches your account to withdraw." })
|
|
13297
|
+
]
|
|
13298
|
+
}
|
|
13299
|
+
),
|
|
13300
|
+
isWalletMatch && connectedWalletName ? /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
|
|
13301
|
+
"button",
|
|
13302
|
+
{
|
|
13303
|
+
type: "button",
|
|
13304
|
+
onClick: handleWithdraw,
|
|
13305
|
+
disabled: !isFormValid || !canWithdraw || isSubmitting || !selectedToken || !selectedChain,
|
|
13306
|
+
className: "uf-w-full uf-py-3 uf-text-sm uf-font-medium uf-transition-colors disabled:uf-opacity-50 disabled:uf-cursor-not-allowed uf-flex uf-items-center uf-justify-center uf-gap-2",
|
|
13307
|
+
style: {
|
|
13308
|
+
backgroundColor: colors2.primary,
|
|
13309
|
+
color: colors2.primaryForeground,
|
|
13310
|
+
fontFamily: fonts.medium,
|
|
13311
|
+
borderRadius: components.button.borderRadius,
|
|
13312
|
+
border: `${components.button.borderWidth}px solid ${components.button.borderColor}`
|
|
13313
|
+
},
|
|
13314
|
+
children: isSubmitting ? /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)(import_jsx_runtime53.Fragment, { children: [
|
|
13315
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)(import_lucide_react24.Loader2, { className: "uf-w-4 uf-h-4 uf-animate-spin" }),
|
|
13316
|
+
"Processing..."
|
|
13317
|
+
] }) : isOverBalance ? /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(import_jsx_runtime53.Fragment, { children: "Insufficient balance" }) : isBelowMinimum ? /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(import_jsx_runtime53.Fragment, { children: "Minimum amount not met" }) : submitError ? /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(import_jsx_runtime53.Fragment, { children: "Withdrawal failed. Try again" }) : /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)(import_jsx_runtime53.Fragment, { children: [
|
|
13318
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)(import_lucide_react24.Wallet, { className: "uf-w-4 uf-h-4" }),
|
|
13319
|
+
"Withdraw from ",
|
|
13320
|
+
connectedWalletName
|
|
13321
|
+
] })
|
|
13322
|
+
}
|
|
13323
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
|
|
13324
|
+
"button",
|
|
13325
|
+
{
|
|
13326
|
+
type: "button",
|
|
13327
|
+
onClick: handleWithdraw,
|
|
13328
|
+
disabled: !isFormValid || !canWithdraw || isSubmitting || !selectedToken || !selectedChain,
|
|
13329
|
+
className: "uf-w-full uf-py-3 uf-text-sm uf-font-medium uf-transition-colors disabled:uf-opacity-50 disabled:uf-cursor-not-allowed",
|
|
13330
|
+
style: {
|
|
13331
|
+
backgroundColor: colors2.primary,
|
|
13332
|
+
color: colors2.primaryForeground,
|
|
13333
|
+
fontFamily: fonts.medium,
|
|
13334
|
+
borderRadius: components.button.borderRadius,
|
|
13335
|
+
border: `${components.button.borderWidth}px solid ${components.button.borderColor}`
|
|
13336
|
+
},
|
|
13337
|
+
children: isSubmitting ? /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)("span", { className: "uf-flex uf-items-center uf-justify-center uf-gap-2", children: [
|
|
13338
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)(import_lucide_react24.Loader2, { className: "uf-w-4 uf-h-4 uf-animate-spin" }),
|
|
13339
|
+
"Processing..."
|
|
13340
|
+
] }) : isOverBalance ? "Insufficient balance" : isBelowMinimum ? "Minimum amount not met" : submitError ? "Withdrawal failed. Try again" : t8.withdraw
|
|
13341
|
+
}
|
|
13342
|
+
),
|
|
13343
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsxs)("div", { className: "uf-flex uf-items-center uf-justify-between uf-text-xs uf-pt-1", children: [
|
|
13344
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)("div", { children: footerLeft }),
|
|
13345
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)(DepositFooterLinks, { onGlossaryClick: () => setGlossaryOpen(true) })
|
|
13346
|
+
] }),
|
|
13347
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
|
|
13348
|
+
GlossaryModal,
|
|
13349
|
+
{
|
|
13350
|
+
open: glossaryOpen,
|
|
13351
|
+
onOpenChange: setGlossaryOpen
|
|
13352
|
+
}
|
|
13353
|
+
)
|
|
13354
|
+
] });
|
|
13355
|
+
}
|
|
13356
|
+
|
|
13357
|
+
// src/components/withdrawals/WithdrawExecutionItem.tsx
|
|
13358
|
+
var import_lucide_react25 = require("lucide-react");
|
|
13359
|
+
var import_core31 = require("@unifold/core");
|
|
13360
|
+
var import_jsx_runtime54 = require("react/jsx-runtime");
|
|
13361
|
+
function WithdrawExecutionItem({
|
|
13362
|
+
execution,
|
|
13363
|
+
onClick
|
|
13364
|
+
}) {
|
|
13365
|
+
const { colors: colors2, fonts, components } = useTheme();
|
|
13366
|
+
const isPending = execution.status === import_core31.ExecutionStatus.PENDING || execution.status === import_core31.ExecutionStatus.WAITING || execution.status === import_core31.ExecutionStatus.DELAYED;
|
|
13367
|
+
const formatDateTime = (timestamp) => {
|
|
13368
|
+
try {
|
|
13369
|
+
const date = new Date(timestamp);
|
|
13370
|
+
const monthDay = date.toLocaleDateString("en-US", {
|
|
13371
|
+
month: "short",
|
|
13372
|
+
day: "numeric",
|
|
13373
|
+
year: "numeric"
|
|
13374
|
+
});
|
|
13375
|
+
const time = date.toLocaleTimeString("en-US", {
|
|
13376
|
+
hour: "numeric",
|
|
13377
|
+
minute: "2-digit",
|
|
13378
|
+
hour12: true
|
|
13379
|
+
}).toLowerCase();
|
|
13380
|
+
return `${monthDay} at ${time}`;
|
|
13381
|
+
} catch {
|
|
13382
|
+
return timestamp;
|
|
13383
|
+
}
|
|
13384
|
+
};
|
|
13385
|
+
const formatUsdAmount2 = (sourceAmountUsd) => {
|
|
13386
|
+
try {
|
|
13387
|
+
const amount = Number(sourceAmountUsd);
|
|
13388
|
+
return new Intl.NumberFormat("en-US", {
|
|
13389
|
+
style: "currency",
|
|
13390
|
+
currency: "USD",
|
|
13391
|
+
minimumFractionDigits: 2,
|
|
13392
|
+
maximumFractionDigits: 2
|
|
13393
|
+
}).format(amount);
|
|
13394
|
+
} catch {
|
|
13395
|
+
return "$0.00";
|
|
13396
|
+
}
|
|
13397
|
+
};
|
|
13398
|
+
return /* @__PURE__ */ (0, import_jsx_runtime54.jsxs)(
|
|
13399
|
+
"button",
|
|
13400
|
+
{
|
|
13401
|
+
onClick,
|
|
13402
|
+
className: "uf-w-full uf-p-3 uf-flex uf-items-center uf-gap-3 hover:uf-bg-secondary/80 uf-transition-colors uf-text-left",
|
|
13403
|
+
style: {
|
|
13404
|
+
backgroundColor: components.card.backgroundColor,
|
|
13405
|
+
borderRadius: components.list.rowBorderRadius,
|
|
13406
|
+
border: `${components.card.borderWidth}px solid ${components.card.borderColor}`
|
|
13407
|
+
},
|
|
13408
|
+
children: [
|
|
13409
|
+
/* @__PURE__ */ (0, import_jsx_runtime54.jsxs)("div", { className: "uf-relative uf-flex-shrink-0 uf-w-9 uf-h-9", children: [
|
|
13410
|
+
/* @__PURE__ */ (0, import_jsx_runtime54.jsx)(
|
|
13411
|
+
"img",
|
|
13412
|
+
{
|
|
13413
|
+
src: execution.destination_token_metadata?.icon_url || (0, import_core31.getIconUrl)("/icons/tokens/svg/usdc.svg"),
|
|
13414
|
+
alt: "Token",
|
|
13415
|
+
width: 36,
|
|
13416
|
+
height: 36,
|
|
13417
|
+
loading: "lazy",
|
|
13418
|
+
className: "uf-rounded-full uf-w-9 uf-h-9"
|
|
13419
|
+
}
|
|
13420
|
+
),
|
|
13421
|
+
isPending ? /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(
|
|
13422
|
+
"div",
|
|
13423
|
+
{
|
|
13424
|
+
className: "uf-absolute -uf-bottom-0.5 -uf-right-0.5 uf-rounded-full uf-p-0.5",
|
|
13425
|
+
style: { backgroundColor: colors2.warning },
|
|
13426
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(
|
|
13427
|
+
"svg",
|
|
13428
|
+
{
|
|
13429
|
+
width: "10",
|
|
13430
|
+
height: "10",
|
|
13431
|
+
viewBox: "0 0 12 12",
|
|
13432
|
+
fill: "none",
|
|
13433
|
+
className: "uf-animate-spin uf-block",
|
|
13434
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(
|
|
13435
|
+
"path",
|
|
13436
|
+
{
|
|
13437
|
+
d: "M6 1V3M6 9V11M1 6H3M9 6H11M2.5 2.5L4 4M8 8L9.5 9.5M2.5 9.5L4 8M8 4L9.5 2.5",
|
|
13438
|
+
stroke: "white",
|
|
13439
|
+
strokeWidth: "2",
|
|
13440
|
+
strokeLinecap: "round"
|
|
13441
|
+
}
|
|
13442
|
+
)
|
|
13443
|
+
}
|
|
13444
|
+
)
|
|
13445
|
+
}
|
|
13446
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(
|
|
13447
|
+
"div",
|
|
13448
|
+
{
|
|
13449
|
+
className: "uf-absolute -uf-bottom-0.5 -uf-right-0.5 uf-rounded-full uf-p-0.5",
|
|
13450
|
+
style: { backgroundColor: colors2.success },
|
|
13451
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(
|
|
13452
|
+
"svg",
|
|
13453
|
+
{
|
|
13454
|
+
width: "10",
|
|
13455
|
+
height: "10",
|
|
13456
|
+
viewBox: "0 0 12 12",
|
|
13457
|
+
fill: "none",
|
|
13458
|
+
className: "uf-block",
|
|
13459
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(
|
|
13460
|
+
"path",
|
|
13461
|
+
{
|
|
13462
|
+
d: "M10 3L4.5 8.5L2 6",
|
|
13463
|
+
stroke: "white",
|
|
13464
|
+
strokeWidth: "2",
|
|
13465
|
+
strokeLinecap: "round",
|
|
13466
|
+
strokeLinejoin: "round"
|
|
13467
|
+
}
|
|
13468
|
+
)
|
|
13469
|
+
}
|
|
13470
|
+
)
|
|
13471
|
+
}
|
|
13472
|
+
)
|
|
13473
|
+
] }),
|
|
13474
|
+
/* @__PURE__ */ (0, import_jsx_runtime54.jsxs)("div", { className: "uf-flex-1 uf-min-w-0", children: [
|
|
13475
|
+
/* @__PURE__ */ (0, import_jsx_runtime54.jsx)(
|
|
13476
|
+
"h3",
|
|
13477
|
+
{
|
|
13478
|
+
className: "uf-font-medium uf-text-sm uf-leading-tight",
|
|
13479
|
+
style: {
|
|
13480
|
+
color: components.card.titleColor,
|
|
13481
|
+
fontFamily: fonts.medium
|
|
13482
|
+
},
|
|
13483
|
+
children: isPending ? "Withdrawal processing" : "Withdrawal completed"
|
|
13484
|
+
}
|
|
13485
|
+
),
|
|
13486
|
+
/* @__PURE__ */ (0, import_jsx_runtime54.jsx)(
|
|
13487
|
+
"p",
|
|
13488
|
+
{
|
|
13489
|
+
className: "uf-text-xs uf-leading-tight",
|
|
13490
|
+
style: {
|
|
13491
|
+
color: components.card.subtitleColor,
|
|
13492
|
+
fontFamily: fonts.regular
|
|
13493
|
+
},
|
|
13494
|
+
children: formatDateTime(execution.created_at || (/* @__PURE__ */ new Date()).toISOString())
|
|
13495
|
+
}
|
|
13496
|
+
)
|
|
13497
|
+
] }),
|
|
13498
|
+
/* @__PURE__ */ (0, import_jsx_runtime54.jsx)(
|
|
13499
|
+
"span",
|
|
13500
|
+
{
|
|
13501
|
+
className: "uf-font-medium uf-text-sm uf-flex-shrink-0",
|
|
13502
|
+
style: {
|
|
13503
|
+
color: components.card.textRightColor,
|
|
13504
|
+
fontFamily: fonts.medium
|
|
13505
|
+
},
|
|
13506
|
+
children: formatUsdAmount2(execution.source_amount_usd || "0")
|
|
13507
|
+
}
|
|
13508
|
+
),
|
|
13509
|
+
/* @__PURE__ */ (0, import_jsx_runtime54.jsx)(
|
|
13510
|
+
import_lucide_react25.ChevronRight,
|
|
13511
|
+
{
|
|
13512
|
+
className: "uf-w-4 uf-h-4 uf-flex-shrink-0",
|
|
13513
|
+
style: { color: components.card.actionColor }
|
|
13514
|
+
}
|
|
13515
|
+
)
|
|
13516
|
+
]
|
|
13517
|
+
}
|
|
13518
|
+
);
|
|
13519
|
+
}
|
|
13520
|
+
|
|
13521
|
+
// src/components/withdrawals/WithdrawConfirmingView.tsx
|
|
13522
|
+
var import_react21 = require("react");
|
|
13523
|
+
var import_jsx_runtime55 = require("react/jsx-runtime");
|
|
13524
|
+
function truncateAddress4(addr) {
|
|
13525
|
+
if (addr.length <= 12) return addr;
|
|
13526
|
+
return `${addr.slice(0, 6)}...${addr.slice(-4)}`;
|
|
13527
|
+
}
|
|
13528
|
+
var SHOW_BUTTON_DELAY_MS = 5e3;
|
|
13529
|
+
function WithdrawConfirmingView({
|
|
13530
|
+
txInfo,
|
|
13531
|
+
executions,
|
|
13532
|
+
onClose,
|
|
13533
|
+
onViewTracker
|
|
13534
|
+
}) {
|
|
13535
|
+
const { colors: colors2, fonts, components } = useTheme();
|
|
13536
|
+
const [showButton, setShowButton] = (0, import_react21.useState)(false);
|
|
13537
|
+
const latestExecution = executions.length > 0 ? executions[executions.length - 1] : null;
|
|
13538
|
+
(0, import_react21.useEffect)(() => {
|
|
13539
|
+
if (latestExecution) return;
|
|
13540
|
+
const timer = setTimeout(() => setShowButton(true), SHOW_BUTTON_DELAY_MS);
|
|
13541
|
+
return () => clearTimeout(timer);
|
|
13542
|
+
}, [latestExecution]);
|
|
13543
|
+
const btnRadius = components.button.borderRadius;
|
|
13544
|
+
const btnBorder = `${components.button.borderWidth}px solid ${components.button.borderColor}`;
|
|
13545
|
+
if (latestExecution) {
|
|
13546
|
+
return /* @__PURE__ */ (0, import_jsx_runtime55.jsxs)(import_jsx_runtime55.Fragment, { children: [
|
|
13547
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)(DepositHeader, { title: "Withdrawal Details", showClose: true, onClose }),
|
|
13548
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)(DepositDetailContent, { execution: latestExecution, variant: "withdraw" }),
|
|
13549
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsxs)("div", { className: "uf-flex uf-gap-2 uf-px-2 uf-pt-2", children: [
|
|
13550
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
|
|
13551
|
+
"button",
|
|
13552
|
+
{
|
|
13553
|
+
type: "button",
|
|
13554
|
+
onClick: onViewTracker,
|
|
13555
|
+
className: "uf-flex-1 uf-py-2.5 uf-text-sm uf-transition-colors hover:uf-opacity-90",
|
|
13556
|
+
style: {
|
|
13557
|
+
backgroundColor: components.button.secondaryBackground,
|
|
13558
|
+
color: components.button.secondaryText,
|
|
13559
|
+
fontFamily: fonts.medium,
|
|
13560
|
+
borderRadius: btnRadius,
|
|
13561
|
+
border: btnBorder
|
|
13562
|
+
},
|
|
13563
|
+
children: "Withdrawal History"
|
|
13564
|
+
}
|
|
13565
|
+
),
|
|
13566
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
|
|
13567
|
+
"button",
|
|
13568
|
+
{
|
|
13569
|
+
type: "button",
|
|
13570
|
+
onClick: onClose,
|
|
13571
|
+
className: "uf-flex-1 uf-py-2.5 uf-text-sm uf-transition-colors hover:uf-opacity-90",
|
|
13572
|
+
style: {
|
|
13573
|
+
backgroundColor: components.button.primaryBackground,
|
|
13574
|
+
color: components.button.primaryText,
|
|
13575
|
+
fontFamily: fonts.medium,
|
|
13576
|
+
borderRadius: btnRadius,
|
|
13577
|
+
border: btnBorder
|
|
13578
|
+
},
|
|
13579
|
+
children: "Close"
|
|
13580
|
+
}
|
|
13581
|
+
)
|
|
13582
|
+
] }),
|
|
13583
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)("div", { className: "uf-pt-3", children: /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
|
|
13584
|
+
PoweredByUnifold,
|
|
13585
|
+
{
|
|
13586
|
+
color: colors2.foregroundMuted,
|
|
13587
|
+
className: "uf-flex uf-justify-center uf-shrink-0"
|
|
13588
|
+
}
|
|
13589
|
+
) })
|
|
13590
|
+
] });
|
|
13591
|
+
}
|
|
13592
|
+
return /* @__PURE__ */ (0, import_jsx_runtime55.jsxs)(import_jsx_runtime55.Fragment, { children: [
|
|
13593
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)(DepositHeader, { title: "Withdrawal Status", showClose: true, onClose }),
|
|
13594
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsxs)("div", { className: "uf-flex uf-flex-col uf-items-center uf-justify-center uf-py-16 uf-px-4", children: [
|
|
13595
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
|
|
13596
|
+
"div",
|
|
13597
|
+
{
|
|
13598
|
+
className: "uf-w-20 uf-h-20 uf-rounded-full uf-flex uf-items-center uf-justify-center uf-mb-6",
|
|
13599
|
+
style: { backgroundColor: `${colors2.primary}20` },
|
|
13600
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
|
|
13601
|
+
"svg",
|
|
13602
|
+
{
|
|
13603
|
+
width: "40",
|
|
13604
|
+
height: "40",
|
|
13605
|
+
viewBox: "0 0 24 24",
|
|
13606
|
+
fill: "none",
|
|
13607
|
+
className: "uf-animate-spin",
|
|
13608
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
|
|
13609
|
+
"path",
|
|
13610
|
+
{
|
|
13611
|
+
d: "M21 12a9 9 0 1 1-6.22-8.56",
|
|
13612
|
+
stroke: colors2.primary,
|
|
13613
|
+
strokeWidth: "2.5",
|
|
13614
|
+
strokeLinecap: "round"
|
|
13615
|
+
}
|
|
13616
|
+
)
|
|
13617
|
+
}
|
|
13618
|
+
)
|
|
13619
|
+
}
|
|
13620
|
+
),
|
|
13621
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
|
|
13622
|
+
"h3",
|
|
13623
|
+
{
|
|
13624
|
+
className: "uf-text-xl uf-mb-2",
|
|
13625
|
+
style: { color: colors2.foreground, fontFamily: fonts.medium },
|
|
13626
|
+
children: "Checking Withdrawal"
|
|
13627
|
+
}
|
|
13628
|
+
),
|
|
13629
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsxs)(
|
|
13630
|
+
"p",
|
|
13631
|
+
{
|
|
13632
|
+
className: "uf-text-sm uf-text-center",
|
|
13633
|
+
style: { color: colors2.foregroundMuted, fontFamily: fonts.regular },
|
|
13634
|
+
children: [
|
|
13635
|
+
txInfo.amount,
|
|
13636
|
+
" ",
|
|
13637
|
+
txInfo.sourceTokenSymbol,
|
|
13638
|
+
" to",
|
|
13639
|
+
" ",
|
|
13640
|
+
truncateAddress4(txInfo.recipientAddress)
|
|
13641
|
+
]
|
|
13642
|
+
}
|
|
13643
|
+
)
|
|
13644
|
+
] }),
|
|
13645
|
+
showButton && /* @__PURE__ */ (0, import_jsx_runtime55.jsx)("div", { className: "uf-px-1 uf-pb-1", children: /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
|
|
13646
|
+
"button",
|
|
13647
|
+
{
|
|
13648
|
+
type: "button",
|
|
13649
|
+
onClick: onViewTracker,
|
|
13650
|
+
className: "uf-w-full uf-py-2.5 uf-text-sm uf-transition-colors hover:uf-opacity-90",
|
|
13651
|
+
style: {
|
|
13652
|
+
backgroundColor: components.button.secondaryBackground,
|
|
13653
|
+
color: components.button.secondaryText,
|
|
13654
|
+
fontFamily: fonts.medium,
|
|
13655
|
+
borderRadius: btnRadius,
|
|
13656
|
+
border: btnBorder
|
|
13657
|
+
},
|
|
13658
|
+
children: "Withdrawal History"
|
|
13659
|
+
}
|
|
13660
|
+
) }),
|
|
13661
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)("div", { className: "uf-pt-3", children: /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
|
|
13662
|
+
PoweredByUnifold,
|
|
13663
|
+
{
|
|
13664
|
+
color: colors2.foregroundMuted,
|
|
13665
|
+
className: "uf-flex uf-justify-center uf-shrink-0"
|
|
13666
|
+
}
|
|
13667
|
+
) })
|
|
13668
|
+
] });
|
|
13669
|
+
}
|
|
13670
|
+
|
|
13671
|
+
// src/components/withdrawals/WithdrawModal.tsx
|
|
13672
|
+
var import_core32 = require("@unifold/core");
|
|
13673
|
+
var import_jsx_runtime56 = require("react/jsx-runtime");
|
|
13674
|
+
var t9 = i18n.withdrawModal;
|
|
13675
|
+
var getChainKey5 = (chainId, chainType) => `${chainType}:${chainId}`;
|
|
13676
|
+
function WithdrawModal({
|
|
13677
|
+
open,
|
|
13678
|
+
onOpenChange,
|
|
13679
|
+
publishableKey,
|
|
13680
|
+
modalTitle,
|
|
13681
|
+
externalUserId,
|
|
13682
|
+
sourceChainType,
|
|
13683
|
+
sourceChainId,
|
|
13684
|
+
sourceTokenAddress,
|
|
13685
|
+
sourceTokenSymbol,
|
|
13686
|
+
recipientAddress: recipientAddressProp,
|
|
13687
|
+
senderAddress,
|
|
13688
|
+
onWithdraw,
|
|
13689
|
+
onWithdrawSuccess,
|
|
13690
|
+
onWithdrawError,
|
|
13691
|
+
theme = "dark",
|
|
13692
|
+
hideOverlay = false
|
|
13693
|
+
}) {
|
|
13694
|
+
const { colors: colors2, fonts, components } = useTheme();
|
|
13695
|
+
const [containerEl, setContainerEl] = (0, import_react22.useState)(null);
|
|
13696
|
+
const containerCallbackRef = (0, import_react22.useCallback)((el) => {
|
|
13697
|
+
setContainerEl(el);
|
|
13698
|
+
}, []);
|
|
13699
|
+
const [resolvedTheme, setResolvedTheme] = (0, import_react22.useState)(
|
|
13700
|
+
theme === "auto" ? "dark" : theme
|
|
13701
|
+
);
|
|
13702
|
+
(0, import_react22.useEffect)(() => {
|
|
13703
|
+
if (theme === "auto") {
|
|
13704
|
+
const mq = window.matchMedia("(prefers-color-scheme: dark)");
|
|
13705
|
+
setResolvedTheme(mq.matches ? "dark" : "light");
|
|
13706
|
+
const h = (e) => setResolvedTheme(e.matches ? "dark" : "light");
|
|
13707
|
+
mq.addEventListener("change", h);
|
|
13708
|
+
return () => mq.removeEventListener("change", h);
|
|
13709
|
+
}
|
|
13710
|
+
setResolvedTheme(theme);
|
|
13711
|
+
}, [theme]);
|
|
13712
|
+
const themeClass = resolvedTheme === "dark" ? "uf-dark" : "";
|
|
13713
|
+
const { data: tokensResponse, isLoading: tokensLoading } = useSupportedDestinationTokens(publishableKey, open);
|
|
13714
|
+
const destinationTokens = tokensResponse?.data ?? [];
|
|
13715
|
+
const { data: sourceValidation, isLoading: isCheckingSourceToken } = useSourceTokenValidation({
|
|
13716
|
+
sourceChainType,
|
|
13717
|
+
sourceChainId,
|
|
13718
|
+
sourceTokenAddress,
|
|
13719
|
+
sourceTokenSymbol,
|
|
13720
|
+
publishableKey,
|
|
13721
|
+
enabled: open
|
|
13722
|
+
});
|
|
13723
|
+
const { data: balanceData, isLoading: isLoadingBalance } = useAddressBalance({
|
|
13724
|
+
address: senderAddress,
|
|
13725
|
+
chainType: sourceChainType,
|
|
13726
|
+
chainId: sourceChainId,
|
|
13727
|
+
tokenAddress: sourceTokenAddress,
|
|
13728
|
+
publishableKey,
|
|
13729
|
+
enabled: open
|
|
13730
|
+
});
|
|
13731
|
+
const [selectedToken, setSelectedToken] = (0, import_react22.useState)(null);
|
|
13732
|
+
const [selectedChain, setSelectedChain] = (0, import_react22.useState)(null);
|
|
13733
|
+
const [detectedWallet, setDetectedWallet] = (0, import_react22.useState)(null);
|
|
13734
|
+
const connectedWalletName = detectedWallet?.name ?? null;
|
|
13735
|
+
const isWalletMatch = !!detectedWallet;
|
|
13736
|
+
(0, import_react22.useEffect)(() => {
|
|
13737
|
+
if (!senderAddress || !open) {
|
|
13738
|
+
setDetectedWallet(null);
|
|
13739
|
+
return;
|
|
13740
|
+
}
|
|
13741
|
+
let cancelled = false;
|
|
13742
|
+
detectBrowserWallet(sourceChainType, senderAddress).then((wallet) => {
|
|
13743
|
+
if (!cancelled) setDetectedWallet(wallet);
|
|
13744
|
+
});
|
|
13745
|
+
return () => {
|
|
13746
|
+
cancelled = true;
|
|
13747
|
+
};
|
|
13748
|
+
}, [senderAddress, sourceChainType, open]);
|
|
13749
|
+
const [view, setView] = (0, import_react22.useState)("form");
|
|
13750
|
+
const [withdrawDepositWalletId, setWithdrawDepositWalletId] = (0, import_react22.useState)();
|
|
13751
|
+
const [selectedExecution, setSelectedExecution] = (0, import_react22.useState)(null);
|
|
13752
|
+
const [submittedTxInfo, setSubmittedTxInfo] = (0, import_react22.useState)(null);
|
|
13753
|
+
const { executions: realtimeExecutions } = useWithdrawPolling({
|
|
13754
|
+
userId: externalUserId,
|
|
13755
|
+
publishableKey,
|
|
13756
|
+
depositWalletId: withdrawDepositWalletId,
|
|
13757
|
+
enabled: !!withdrawDepositWalletId && open,
|
|
13758
|
+
onWithdrawSuccess: onWithdrawSuccess ? (d) => onWithdrawSuccess({ message: d.message, transaction: d.transaction }) : void 0,
|
|
13759
|
+
onWithdrawError
|
|
13760
|
+
});
|
|
13761
|
+
const { data: allWithdrawalsData } = useExecutions(externalUserId, publishableKey, {
|
|
13762
|
+
actionType: import_core32.ActionType.Withdraw,
|
|
13763
|
+
enabled: open,
|
|
13764
|
+
refetchInterval: view === "tracker" || view === "detail" ? 5e3 : 15e3
|
|
13765
|
+
});
|
|
13766
|
+
const allWithdrawals = allWithdrawalsData?.data ?? [];
|
|
13767
|
+
const handleDepositWalletCreation = (0, import_react22.useCallback)(async (params) => {
|
|
13768
|
+
const { data: wallets } = await (0, import_core32.createDepositAddress)(
|
|
13769
|
+
{
|
|
13770
|
+
external_user_id: externalUserId,
|
|
13771
|
+
destination_chain_type: params.destinationChainType,
|
|
13772
|
+
destination_chain_id: params.destinationChainId,
|
|
13773
|
+
destination_token_address: params.destinationTokenAddress,
|
|
13774
|
+
recipient_address: params.recipientAddress,
|
|
13775
|
+
action_type: import_core32.ActionType.Withdraw
|
|
13776
|
+
},
|
|
13777
|
+
publishableKey
|
|
13778
|
+
);
|
|
13779
|
+
const depositWallet = (0, import_core32.getWalletByChainType)(wallets, sourceChainType);
|
|
13780
|
+
if (!depositWallet) {
|
|
13781
|
+
throw new Error(`No deposit wallet available for ${sourceChainType}`);
|
|
13782
|
+
}
|
|
13783
|
+
setWithdrawDepositWalletId(depositWallet.id);
|
|
13784
|
+
return depositWallet;
|
|
13785
|
+
}, [externalUserId, publishableKey, sourceChainType]);
|
|
13786
|
+
const handleWithdrawSubmitted = (0, import_react22.useCallback)((txInfo) => {
|
|
13787
|
+
setSubmittedTxInfo(txInfo);
|
|
13788
|
+
setView("confirming");
|
|
13789
|
+
}, []);
|
|
13790
|
+
(0, import_react22.useEffect)(() => {
|
|
13791
|
+
if (!destinationTokens.length || selectedToken) return;
|
|
13792
|
+
const first = destinationTokens[0];
|
|
13793
|
+
if (first?.chains.length > 0) {
|
|
13794
|
+
setSelectedToken(first);
|
|
13795
|
+
setSelectedChain(first.chains[0]);
|
|
13796
|
+
}
|
|
13797
|
+
}, [destinationTokens, selectedToken]);
|
|
13798
|
+
const resetViewTimeoutRef = (0, import_react22.useRef)(null);
|
|
13799
|
+
const handleClose = (0, import_react22.useCallback)(() => {
|
|
13800
|
+
onOpenChange(false);
|
|
13801
|
+
if (resetViewTimeoutRef.current) clearTimeout(resetViewTimeoutRef.current);
|
|
13802
|
+
resetViewTimeoutRef.current = setTimeout(() => {
|
|
13803
|
+
setSelectedToken(null);
|
|
13804
|
+
setSelectedChain(null);
|
|
13805
|
+
setView("form");
|
|
13806
|
+
setSelectedExecution(null);
|
|
13807
|
+
setSubmittedTxInfo(null);
|
|
13808
|
+
setWithdrawDepositWalletId(void 0);
|
|
13809
|
+
resetViewTimeoutRef.current = null;
|
|
13810
|
+
}, 200);
|
|
13811
|
+
}, [onOpenChange]);
|
|
13812
|
+
(0, import_react22.useLayoutEffect)(() => {
|
|
13813
|
+
if (!open) return;
|
|
13814
|
+
if (resetViewTimeoutRef.current) {
|
|
13815
|
+
clearTimeout(resetViewTimeoutRef.current);
|
|
13816
|
+
resetViewTimeoutRef.current = null;
|
|
13817
|
+
}
|
|
13818
|
+
setSelectedToken(null);
|
|
13819
|
+
setSelectedChain(null);
|
|
13820
|
+
setView("form");
|
|
13821
|
+
setSelectedExecution(null);
|
|
13822
|
+
setSubmittedTxInfo(null);
|
|
13823
|
+
setWithdrawDepositWalletId(void 0);
|
|
13824
|
+
}, [open]);
|
|
13825
|
+
(0, import_react22.useEffect)(() => () => {
|
|
13826
|
+
if (resetViewTimeoutRef.current) clearTimeout(resetViewTimeoutRef.current);
|
|
13827
|
+
}, []);
|
|
13828
|
+
const handleTokenSymbolChange = (0, import_react22.useCallback)((symbol) => {
|
|
13829
|
+
const tok = destinationTokens.find((t11) => t11.symbol === symbol);
|
|
13830
|
+
if (tok) {
|
|
13831
|
+
setSelectedToken(tok);
|
|
13832
|
+
if (tok.chains.length > 0) setSelectedChain(tok.chains[0]);
|
|
13833
|
+
}
|
|
13834
|
+
}, [destinationTokens]);
|
|
13835
|
+
const handleChainKeyChange = (0, import_react22.useCallback)((chainKey) => {
|
|
13836
|
+
if (!selectedToken) return;
|
|
13837
|
+
const chain = selectedToken.chains.find((c) => getChainKey5(c.chain_id, c.chain_type) === chainKey);
|
|
13838
|
+
if (chain) setSelectedChain(chain);
|
|
13839
|
+
}, [selectedToken]);
|
|
13840
|
+
const isSourceSupported = sourceValidation?.isSupported ?? null;
|
|
13841
|
+
const canWithdraw = !!onWithdraw || isWalletMatch;
|
|
13842
|
+
const isAnyLoading = tokensLoading || isCheckingSourceToken;
|
|
13843
|
+
const withdrawPoweredByFooter = /* @__PURE__ */ (0, import_jsx_runtime56.jsx)("div", { className: "uf-pt-3", children: /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(PoweredByUnifold, { color: colors2.foregroundMuted, className: "uf-flex uf-justify-center uf-shrink-0" }) });
|
|
13844
|
+
return /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(PortalContainerProvider, { value: hideOverlay ? containerEl : null, children: /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(Dialog, { open: hideOverlay || open, onOpenChange: hideOverlay ? void 0 : handleClose, modal: !hideOverlay, children: /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(
|
|
13845
|
+
DialogContent,
|
|
13846
|
+
{
|
|
13847
|
+
ref: hideOverlay ? containerCallbackRef : void 0,
|
|
13848
|
+
hideOverlay,
|
|
13849
|
+
className: `sm:uf-max-w-[400px] uf-border-secondary uf-text-foreground uf-gap-0 [&>button]:uf-hidden ${hideOverlay ? `uf-p-6 uf-overflow-hidden ${themeClass}` : `uf-p-0 uf-overflow-visible !uf-top-auto !uf-h-auto !uf-max-h-[90vh] sm:!uf-max-h-none sm:!uf-top-[50%] ${themeClass}`}`,
|
|
13850
|
+
style: { backgroundColor: colors2.background },
|
|
13851
|
+
onPointerDownOutside: (e) => e.preventDefault(),
|
|
13852
|
+
onInteractOutside: (e) => e.preventDefault(),
|
|
13853
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(ThemeStyleInjector, { children: view === "confirming" && submittedTxInfo ? /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(
|
|
13854
|
+
WithdrawConfirmingView,
|
|
13855
|
+
{
|
|
13856
|
+
txInfo: submittedTxInfo,
|
|
13857
|
+
executions: realtimeExecutions,
|
|
13858
|
+
onClose: handleClose,
|
|
13859
|
+
onViewTracker: () => setView("tracker")
|
|
13860
|
+
}
|
|
13861
|
+
) : view === "detail" && selectedExecution ? /* @__PURE__ */ (0, import_jsx_runtime56.jsxs)(import_jsx_runtime56.Fragment, { children: [
|
|
13862
|
+
/* @__PURE__ */ (0, import_jsx_runtime56.jsx)(DepositHeader, { title: "Withdrawal Details", showBack: true, showClose: !hideOverlay, onBack: () => {
|
|
13863
|
+
setSelectedExecution(null);
|
|
13864
|
+
setView("tracker");
|
|
13865
|
+
}, onClose: handleClose }),
|
|
13866
|
+
/* @__PURE__ */ (0, import_jsx_runtime56.jsx)(DepositDetailContent, { execution: selectedExecution, variant: "withdraw" }),
|
|
13867
|
+
withdrawPoweredByFooter
|
|
13868
|
+
] }) : view === "tracker" ? (
|
|
13869
|
+
/* ---------- Tracker view: execution list ---------- */
|
|
13870
|
+
/* @__PURE__ */ (0, import_jsx_runtime56.jsxs)(import_jsx_runtime56.Fragment, { children: [
|
|
13871
|
+
/* @__PURE__ */ (0, import_jsx_runtime56.jsx)(DepositHeader, { title: "Withdrawal History", showBack: true, showClose: !hideOverlay, onBack: () => setView("form"), onClose: handleClose }),
|
|
13872
|
+
/* @__PURE__ */ (0, import_jsx_runtime56.jsx)("div", { className: "uf-flex uf-flex-col uf-gap-2", style: { minHeight: 200 }, children: allWithdrawals.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime56.jsx)("div", { className: "uf-flex uf-items-center uf-justify-center uf-py-8", children: /* @__PURE__ */ (0, import_jsx_runtime56.jsx)("p", { className: "uf-text-sm", style: { color: colors2.foregroundMuted, fontFamily: fonts.regular }, children: "No withdrawals to track yet" }) }) : allWithdrawals.map((ex) => /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(
|
|
13873
|
+
WithdrawExecutionItem,
|
|
13874
|
+
{
|
|
13875
|
+
execution: ex,
|
|
13876
|
+
onClick: () => {
|
|
13877
|
+
setSelectedExecution(ex);
|
|
13878
|
+
setView("detail");
|
|
13879
|
+
}
|
|
13880
|
+
},
|
|
13881
|
+
ex.id
|
|
13882
|
+
)) }),
|
|
13883
|
+
withdrawPoweredByFooter
|
|
13884
|
+
] })
|
|
13885
|
+
) : (
|
|
13886
|
+
/* ---------- Form view (default) ---------- */
|
|
13887
|
+
/* @__PURE__ */ (0, import_jsx_runtime56.jsxs)(import_jsx_runtime56.Fragment, { children: [
|
|
13888
|
+
/* @__PURE__ */ (0, import_jsx_runtime56.jsx)(DepositHeader, { title: modalTitle || t9.title, showClose: !hideOverlay, onClose: handleClose }),
|
|
13889
|
+
/* @__PURE__ */ (0, import_jsx_runtime56.jsxs)("div", { className: "uf-flex uf-flex-col uf-gap-3", children: [
|
|
13890
|
+
isAnyLoading ? /* @__PURE__ */ (0, import_jsx_runtime56.jsx)("div", { className: "uf-space-y-3", children: [1, 2, 3].map((i) => /* @__PURE__ */ (0, import_jsx_runtime56.jsx)("div", { className: "uf-w-full uf-bg-secondary uf-rounded-xl uf-p-3 uf-flex uf-items-center uf-animate-pulse", children: /* @__PURE__ */ (0, import_jsx_runtime56.jsx)("div", { className: "uf-bg-muted uf-rounded-lg uf-w-full uf-h-10" }) }, i)) }) : isSourceSupported === false ? /* @__PURE__ */ (0, import_jsx_runtime56.jsxs)("div", { className: "uf-flex uf-flex-col uf-items-center uf-justify-center uf-py-8 uf-px-4 uf-text-center", children: [
|
|
13891
|
+
/* @__PURE__ */ (0, import_jsx_runtime56.jsx)("div", { className: "uf-w-16 uf-h-16 uf-rounded-full uf-bg-muted uf-flex uf-items-center uf-justify-center uf-mb-4", children: /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(import_lucide_react26.AlertTriangle, { className: "uf-w-8 uf-h-8 uf-text-muted-foreground" }) }),
|
|
13892
|
+
/* @__PURE__ */ (0, import_jsx_runtime56.jsx)("h3", { className: "uf-text-lg uf-font-semibold uf-mb-2", style: { color: colors2.foreground, fontFamily: fonts.medium }, children: "Unsupported Source Token" }),
|
|
13893
|
+
/* @__PURE__ */ (0, import_jsx_runtime56.jsx)("p", { className: "uf-text-sm uf-max-w-[280px]", style: { color: colors2.foregroundMuted, fontFamily: fonts.regular }, children: sourceValidation?.errorMessage })
|
|
13894
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime56.jsxs)(import_jsx_runtime56.Fragment, { children: [
|
|
13895
|
+
/* @__PURE__ */ (0, import_jsx_runtime56.jsx)(
|
|
13896
|
+
WithdrawDoubleInput,
|
|
13897
|
+
{
|
|
13898
|
+
tokens: destinationTokens,
|
|
13899
|
+
selectedTokenSymbol: selectedToken?.symbol ?? null,
|
|
13900
|
+
selectedChainKey: selectedChain ? getChainKey5(selectedChain.chain_id, selectedChain.chain_type) : null,
|
|
13901
|
+
onTokenChange: handleTokenSymbolChange,
|
|
13902
|
+
onChainChange: handleChainKeyChange,
|
|
13903
|
+
isLoading: tokensLoading
|
|
13904
|
+
}
|
|
13905
|
+
),
|
|
13906
|
+
/* @__PURE__ */ (0, import_jsx_runtime56.jsx)(
|
|
13907
|
+
WithdrawForm,
|
|
13908
|
+
{
|
|
13909
|
+
publishableKey,
|
|
13910
|
+
externalUserId,
|
|
13911
|
+
sourceChainType,
|
|
13912
|
+
selectedToken,
|
|
13913
|
+
selectedChain,
|
|
13914
|
+
sourceTokenSymbol,
|
|
13915
|
+
recipientAddressProp,
|
|
13916
|
+
balanceData: balanceData ?? null,
|
|
13917
|
+
isLoadingBalance,
|
|
13918
|
+
minimumWithdrawAmountUsd: sourceValidation?.minimumAmountUsd ?? null,
|
|
13919
|
+
estimatedProcessingTime: sourceValidation?.estimatedProcessingTime ?? null,
|
|
13920
|
+
maxSlippagePercent: sourceValidation?.maxSlippagePercent ?? null,
|
|
13921
|
+
priceImpactPercent: sourceValidation?.priceImpactPercent ?? null,
|
|
13922
|
+
detectedWallet,
|
|
13923
|
+
sourceChainId,
|
|
13924
|
+
sourceTokenAddress,
|
|
13925
|
+
isWalletMatch,
|
|
13926
|
+
connectedWalletName,
|
|
13927
|
+
canWithdraw,
|
|
13928
|
+
onWithdraw,
|
|
13929
|
+
onWithdrawError,
|
|
13930
|
+
onDepositWalletCreation: handleDepositWalletCreation,
|
|
13931
|
+
onWithdrawSubmitted: handleWithdrawSubmitted,
|
|
13932
|
+
footerLeft: /* @__PURE__ */ (0, import_jsx_runtime56.jsxs)(
|
|
13933
|
+
"button",
|
|
13934
|
+
{
|
|
13935
|
+
onClick: () => setView("tracker"),
|
|
13936
|
+
className: "uf-flex uf-items-center uf-gap-1 uf-transition-colors hover:uf-opacity-70",
|
|
13937
|
+
style: { color: colors2.foregroundMuted },
|
|
13938
|
+
children: [
|
|
13939
|
+
/* @__PURE__ */ (0, import_jsx_runtime56.jsx)(import_lucide_react26.Clock, { className: "uf-w-3.5 uf-h-3.5" }),
|
|
13940
|
+
"Withdrawal History",
|
|
13941
|
+
/* @__PURE__ */ (0, import_jsx_runtime56.jsx)(import_lucide_react26.ChevronRight, { className: "uf-w-3 uf-h-3" })
|
|
13942
|
+
]
|
|
13943
|
+
}
|
|
13944
|
+
)
|
|
13945
|
+
}
|
|
13946
|
+
)
|
|
13947
|
+
] }),
|
|
13948
|
+
withdrawPoweredByFooter
|
|
13949
|
+
] })
|
|
13950
|
+
] })
|
|
13951
|
+
) })
|
|
13952
|
+
}
|
|
13953
|
+
) }) });
|
|
13954
|
+
}
|
|
13955
|
+
|
|
13956
|
+
// src/components/withdrawals/WithdrawTokenSelector.tsx
|
|
13957
|
+
var import_react23 = require("react");
|
|
13958
|
+
var import_lucide_react27 = require("lucide-react");
|
|
13959
|
+
var import_jsx_runtime57 = require("react/jsx-runtime");
|
|
13960
|
+
var t10 = i18n.withdrawModal;
|
|
13961
|
+
function WithdrawTokenSelector({
|
|
13962
|
+
tokens,
|
|
13963
|
+
onSelect,
|
|
13964
|
+
onBack
|
|
13965
|
+
}) {
|
|
13966
|
+
const { themeClass, colors: colors2, fonts, components } = useTheme();
|
|
13967
|
+
const [searchQuery, setSearchQuery] = (0, import_react23.useState)("");
|
|
13968
|
+
const [hoveredKey, setHoveredKey] = (0, import_react23.useState)(null);
|
|
13969
|
+
const allOptions = (0, import_react23.useMemo)(() => {
|
|
13970
|
+
const options = [];
|
|
13971
|
+
tokens.forEach((token) => {
|
|
13972
|
+
token.chains.forEach((chain) => {
|
|
13973
|
+
options.push({ token, chain });
|
|
13974
|
+
});
|
|
13975
|
+
});
|
|
13976
|
+
return options;
|
|
13977
|
+
}, [tokens]);
|
|
13978
|
+
const filteredOptions = (0, import_react23.useMemo)(() => {
|
|
13979
|
+
if (!searchQuery.trim()) return allOptions;
|
|
13980
|
+
const query = searchQuery.toLowerCase();
|
|
13981
|
+
return allOptions.filter(
|
|
13982
|
+
({ token, chain }) => token.symbol.toLowerCase().includes(query) || token.name.toLowerCase().includes(query) || chain.chain_name.toLowerCase().includes(query)
|
|
13983
|
+
);
|
|
13984
|
+
}, [allOptions, searchQuery]);
|
|
13985
|
+
return /* @__PURE__ */ (0, import_jsx_runtime57.jsxs)(
|
|
13986
|
+
"div",
|
|
13987
|
+
{
|
|
13988
|
+
className: "uf-flex uf-flex-col",
|
|
13989
|
+
style: { minHeight: 0, flex: 1 },
|
|
13990
|
+
children: [
|
|
13991
|
+
/* @__PURE__ */ (0, import_jsx_runtime57.jsxs)("div", { className: "uf-pb-3", children: [
|
|
13992
|
+
/* @__PURE__ */ (0, import_jsx_runtime57.jsx)(
|
|
13993
|
+
"style",
|
|
13994
|
+
{
|
|
13995
|
+
dangerouslySetInnerHTML: {
|
|
13996
|
+
__html: `.uf-withdraw-token-search::placeholder { color: ${components.search.placeholderColor}; }`
|
|
13997
|
+
}
|
|
13998
|
+
}
|
|
13999
|
+
),
|
|
14000
|
+
/* @__PURE__ */ (0, import_jsx_runtime57.jsxs)("div", { style: { position: "relative" }, children: [
|
|
14001
|
+
/* @__PURE__ */ (0, import_jsx_runtime57.jsx)(
|
|
14002
|
+
import_lucide_react27.Search,
|
|
14003
|
+
{
|
|
14004
|
+
className: "uf-absolute uf-left-3 uf-top-1/2 uf--translate-y-1/2 uf-w-4 uf-h-4",
|
|
14005
|
+
style: { color: components.search.placeholderColor }
|
|
14006
|
+
}
|
|
14007
|
+
),
|
|
14008
|
+
/* @__PURE__ */ (0, import_jsx_runtime57.jsx)(
|
|
14009
|
+
"input",
|
|
14010
|
+
{
|
|
14011
|
+
type: "text",
|
|
14012
|
+
placeholder: "Search token or network",
|
|
14013
|
+
value: searchQuery,
|
|
14014
|
+
onChange: (e) => setSearchQuery(e.target.value),
|
|
14015
|
+
className: "uf-withdraw-token-search uf-w-full uf-pl-10 uf-pr-4 uf-py-2.5 uf-text-sm uf-outline-none focus:uf-ring-2 focus:uf-ring-ring/30",
|
|
14016
|
+
style: {
|
|
14017
|
+
backgroundColor: components.search.backgroundColor,
|
|
14018
|
+
color: components.search.inputColor,
|
|
14019
|
+
fontFamily: fonts.regular,
|
|
14020
|
+
borderRadius: components.input.borderRadius,
|
|
14021
|
+
border: `${components.input.borderWidth}px solid ${components.input.borderColor}`
|
|
14022
|
+
}
|
|
14023
|
+
}
|
|
14024
|
+
)
|
|
14025
|
+
] })
|
|
14026
|
+
] }),
|
|
14027
|
+
/* @__PURE__ */ (0, import_jsx_runtime57.jsx)(
|
|
14028
|
+
"div",
|
|
14029
|
+
{
|
|
14030
|
+
className: "uf-text-xs uf-mb-2",
|
|
14031
|
+
style: {
|
|
14032
|
+
color: components.list.titleSectionColor,
|
|
14033
|
+
fontFamily: fonts.medium
|
|
14034
|
+
},
|
|
14035
|
+
children: t10.selectToken
|
|
14036
|
+
}
|
|
14037
|
+
),
|
|
14038
|
+
/* @__PURE__ */ (0, import_jsx_runtime57.jsx)(
|
|
14039
|
+
"div",
|
|
14040
|
+
{
|
|
14041
|
+
className: "uf-flex-1 uf-overflow-y-auto uf-min-h-0 uf--mx-6 uf-px-6 uf-pb-3",
|
|
14042
|
+
style: { scrollbarWidth: "none" },
|
|
14043
|
+
children: filteredOptions.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(
|
|
14044
|
+
"div",
|
|
14045
|
+
{
|
|
14046
|
+
style: {
|
|
14047
|
+
textAlign: "center",
|
|
14048
|
+
padding: "2rem 0",
|
|
14049
|
+
fontSize: 14,
|
|
14050
|
+
color: components.container.subtitleColor,
|
|
14051
|
+
fontFamily: fonts.regular
|
|
14052
|
+
},
|
|
14053
|
+
children: t10.noTokensAvailable
|
|
14054
|
+
}
|
|
14055
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime57.jsx)("div", { style: { display: "flex", flexDirection: "column", gap: 4 }, children: filteredOptions.map(({ token, chain }) => {
|
|
14056
|
+
const key = `${token.symbol}-${chain.chain_type}:${chain.chain_id}`;
|
|
14057
|
+
return /* @__PURE__ */ (0, import_jsx_runtime57.jsxs)(
|
|
14058
|
+
"button",
|
|
14059
|
+
{
|
|
14060
|
+
type: "button",
|
|
14061
|
+
onClick: () => onSelect(token, chain),
|
|
14062
|
+
onMouseEnter: () => setHoveredKey(key),
|
|
14063
|
+
onMouseLeave: () => setHoveredKey(null),
|
|
14064
|
+
className: "uf-transition-colors",
|
|
14065
|
+
style: {
|
|
14066
|
+
width: "100%",
|
|
14067
|
+
display: "flex",
|
|
14068
|
+
alignItems: "center",
|
|
14069
|
+
gap: 12,
|
|
14070
|
+
padding: 12,
|
|
14071
|
+
borderRadius: 12,
|
|
14072
|
+
border: "none",
|
|
14073
|
+
cursor: "pointer",
|
|
14074
|
+
textAlign: "left",
|
|
14075
|
+
backgroundColor: hoveredKey === key ? colors2.cardHover : "transparent"
|
|
14076
|
+
},
|
|
14077
|
+
children: [
|
|
14078
|
+
/* @__PURE__ */ (0, import_jsx_runtime57.jsxs)("div", { style: { position: "relative", flexShrink: 0 }, children: [
|
|
14079
|
+
/* @__PURE__ */ (0, import_jsx_runtime57.jsx)(
|
|
14080
|
+
"img",
|
|
14081
|
+
{
|
|
14082
|
+
src: token.icon_url,
|
|
14083
|
+
alt: token.symbol,
|
|
14084
|
+
width: 40,
|
|
14085
|
+
height: 40,
|
|
14086
|
+
loading: "lazy",
|
|
14087
|
+
className: "uf-rounded-full"
|
|
14088
|
+
}
|
|
14089
|
+
),
|
|
14090
|
+
/* @__PURE__ */ (0, import_jsx_runtime57.jsx)(
|
|
14091
|
+
"div",
|
|
14092
|
+
{
|
|
14093
|
+
style: {
|
|
14094
|
+
position: "absolute",
|
|
14095
|
+
bottom: -4,
|
|
14096
|
+
right: -4
|
|
14097
|
+
},
|
|
14098
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(
|
|
14099
|
+
"img",
|
|
14100
|
+
{
|
|
14101
|
+
src: chain.icon_url,
|
|
14102
|
+
alt: chain.chain_name,
|
|
14103
|
+
width: 20,
|
|
14104
|
+
height: 20,
|
|
14105
|
+
loading: "lazy",
|
|
14106
|
+
className: "uf-rounded-full uf-border-2"
|
|
14107
|
+
}
|
|
14108
|
+
)
|
|
14109
|
+
}
|
|
14110
|
+
)
|
|
14111
|
+
] }),
|
|
14112
|
+
/* @__PURE__ */ (0, import_jsx_runtime57.jsxs)("div", { style: { flex: 1, minWidth: 0 }, children: [
|
|
14113
|
+
/* @__PURE__ */ (0, import_jsx_runtime57.jsx)(
|
|
14114
|
+
"div",
|
|
14115
|
+
{
|
|
14116
|
+
style: {
|
|
14117
|
+
fontSize: 14,
|
|
14118
|
+
fontWeight: 500,
|
|
14119
|
+
color: components.card.titleColor,
|
|
14120
|
+
fontFamily: fonts.medium
|
|
14121
|
+
},
|
|
14122
|
+
children: token.symbol
|
|
14123
|
+
}
|
|
14124
|
+
),
|
|
14125
|
+
/* @__PURE__ */ (0, import_jsx_runtime57.jsxs)(
|
|
14126
|
+
"div",
|
|
14127
|
+
{
|
|
14128
|
+
style: {
|
|
14129
|
+
fontSize: 12,
|
|
14130
|
+
color: components.card.subtitleColor,
|
|
14131
|
+
fontFamily: fonts.regular
|
|
14132
|
+
},
|
|
14133
|
+
children: [
|
|
14134
|
+
token.name,
|
|
14135
|
+
" \u2022 ",
|
|
14136
|
+
chain.chain_name
|
|
14137
|
+
]
|
|
14138
|
+
}
|
|
14139
|
+
)
|
|
14140
|
+
] })
|
|
14141
|
+
]
|
|
14142
|
+
},
|
|
14143
|
+
key
|
|
14144
|
+
);
|
|
14145
|
+
}) })
|
|
14146
|
+
}
|
|
14147
|
+
),
|
|
14148
|
+
/* @__PURE__ */ (0, import_jsx_runtime57.jsx)("div", { className: "uf-pt-3 uf-pb-2 uf-shrink-0", children: /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(
|
|
14149
|
+
PoweredByUnifold,
|
|
14150
|
+
{
|
|
14151
|
+
color: colors2.foregroundMuted,
|
|
14152
|
+
className: "uf-flex uf-justify-center uf-shrink-0"
|
|
14153
|
+
}
|
|
14154
|
+
) })
|
|
14155
|
+
]
|
|
14156
|
+
}
|
|
14157
|
+
);
|
|
14158
|
+
}
|
|
12146
14159
|
// Annotate the CommonJS export names for ESM import in node:
|
|
12147
14160
|
0 && (module.exports = {
|
|
12148
14161
|
Button,
|
|
@@ -12189,15 +14202,29 @@ function DepositModal({
|
|
|
12189
14202
|
TransferCryptoButton,
|
|
12190
14203
|
TransferCryptoDoubleInput,
|
|
12191
14204
|
TransferCryptoSingleInput,
|
|
14205
|
+
WithdrawConfirmingView,
|
|
14206
|
+
WithdrawDoubleInput,
|
|
14207
|
+
WithdrawExecutionItem,
|
|
14208
|
+
WithdrawForm,
|
|
14209
|
+
WithdrawModal,
|
|
14210
|
+
WithdrawTokenSelector,
|
|
12192
14211
|
buttonVariants,
|
|
12193
14212
|
cn,
|
|
12194
14213
|
colors,
|
|
12195
14214
|
defaultColors,
|
|
14215
|
+
detectBrowserWallet,
|
|
12196
14216
|
getColors,
|
|
12197
14217
|
mergeColors,
|
|
12198
14218
|
resolveComponentTokens,
|
|
14219
|
+
sendEvmWithdraw,
|
|
14220
|
+
sendSolanaWithdraw,
|
|
12199
14221
|
truncateAddress,
|
|
14222
|
+
useAddressBalance,
|
|
12200
14223
|
useAllowedCountry,
|
|
12201
14224
|
useDepositPolling,
|
|
12202
|
-
|
|
14225
|
+
useSourceTokenValidation,
|
|
14226
|
+
useSupportedDestinationTokens,
|
|
14227
|
+
useTheme,
|
|
14228
|
+
useVerifyRecipientAddress,
|
|
14229
|
+
useWithdrawPolling
|
|
12203
14230
|
});
|