@unifold/ui-react 0.1.40 → 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.js CHANGED
@@ -74,22 +74,36 @@ __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
- useTheme: () => useTheme
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
 
91
105
  // src/components/deposits/DepositModal.tsx
92
- var import_react17 = require("react");
106
+ var import_react18 = require("react");
93
107
  var import_lucide_react23 = require("lucide-react");
94
108
 
95
109
  // src/components/shared/dialog.tsx
@@ -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)(
@@ -7095,7 +7137,7 @@ function useSupportedDepositTokens(publishableKey, options) {
7095
7137
  }
7096
7138
 
7097
7139
  // src/components/deposits/TransferCryptoSingleInput.tsx
7098
- var import_react14 = require("react");
7140
+ var import_react15 = require("react");
7099
7141
  var import_lucide_react16 = require("lucide-react");
7100
7142
 
7101
7143
  // src/components/deposits/StyledQRCode.tsx
@@ -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
- (t7) => !(t7.symbol === token.symbol && t7.chainType === token.chainType && t7.chainId === token.chainId)
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
- (t7) => !(t7.symbol === token.symbol && t7.chainType === token.chainType && t7.chainId === token.chainId)
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((t7) => t7.symbol === symbol);
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
@@ -7972,6 +8014,107 @@ function DepositPollingUi({
7972
8014
  return null;
7973
8015
  }
7974
8016
 
8017
+ // src/hooks/use-default-source-token.ts
8018
+ var import_react13 = require("react");
8019
+ var getChainKey = (chainId, chainType) => {
8020
+ return `${chainType}:${chainId}`;
8021
+ };
8022
+ function resolveSourceToken(supportedTokens, defaultSourceChainType, defaultSourceChainId, defaultSourceTokenAddress, defaultSourceSymbol) {
8023
+ if (!supportedTokens.length) return null;
8024
+ let selectedTokenData;
8025
+ let selectedChainData;
8026
+ const hasChainDefaults = defaultSourceChainType && defaultSourceChainId;
8027
+ if (defaultSourceTokenAddress && hasChainDefaults) {
8028
+ for (const t11 of supportedTokens) {
8029
+ const matchingChain = t11.chains.find(
8030
+ (c) => c.token_address.toLowerCase() === defaultSourceTokenAddress.toLowerCase() && c.chain_type === defaultSourceChainType && c.chain_id === defaultSourceChainId
8031
+ );
8032
+ if (matchingChain) {
8033
+ selectedTokenData = t11;
8034
+ selectedChainData = matchingChain;
8035
+ break;
8036
+ }
8037
+ }
8038
+ }
8039
+ if (!selectedTokenData && defaultSourceSymbol && hasChainDefaults) {
8040
+ for (const t11 of supportedTokens) {
8041
+ if (t11.symbol !== defaultSourceSymbol) continue;
8042
+ const matchedChain = t11.chains.find(
8043
+ (c) => c.chain_type === defaultSourceChainType && c.chain_id === defaultSourceChainId
8044
+ );
8045
+ if (matchedChain) {
8046
+ selectedTokenData = t11;
8047
+ selectedChainData = matchedChain;
8048
+ break;
8049
+ }
8050
+ }
8051
+ }
8052
+ if (!selectedTokenData) {
8053
+ for (const t11 of supportedTokens) {
8054
+ if (t11.chains.length > 0) {
8055
+ selectedTokenData = t11;
8056
+ selectedChainData = t11.chains[0];
8057
+ break;
8058
+ }
8059
+ }
8060
+ }
8061
+ if (selectedTokenData && selectedChainData) {
8062
+ return { token: selectedTokenData, chain: selectedChainData };
8063
+ }
8064
+ return null;
8065
+ }
8066
+ function useDefaultSourceToken({
8067
+ supportedTokens,
8068
+ defaultSourceChainType,
8069
+ defaultSourceChainId,
8070
+ defaultSourceTokenAddress,
8071
+ defaultSourceSymbol
8072
+ }) {
8073
+ const [token, setToken] = (0, import_react13.useState)(null);
8074
+ const [chain, setChain] = (0, import_react13.useState)(null);
8075
+ const [initialSelectionDone, setInitialSelectionDone] = (0, import_react13.useState)(false);
8076
+ const appliedDefaultsRef = (0, import_react13.useRef)("");
8077
+ (0, import_react13.useEffect)(() => {
8078
+ if (!supportedTokens.length) return;
8079
+ const defaultsKey = `${defaultSourceTokenAddress ?? ""}|${defaultSourceSymbol ?? ""}|${defaultSourceChainType ?? ""}|${defaultSourceChainId ?? ""}`;
8080
+ const defaultsChanged = appliedDefaultsRef.current !== defaultsKey;
8081
+ if (initialSelectionDone && !defaultsChanged) return;
8082
+ const result = resolveSourceToken(
8083
+ supportedTokens,
8084
+ defaultSourceChainType,
8085
+ defaultSourceChainId,
8086
+ defaultSourceTokenAddress,
8087
+ defaultSourceSymbol
8088
+ );
8089
+ if (result) {
8090
+ setToken(result.token.symbol);
8091
+ setChain(getChainKey(result.chain.chain_id, result.chain.chain_type));
8092
+ appliedDefaultsRef.current = defaultsKey;
8093
+ setInitialSelectionDone(true);
8094
+ }
8095
+ }, [
8096
+ supportedTokens,
8097
+ defaultSourceTokenAddress,
8098
+ defaultSourceSymbol,
8099
+ defaultSourceChainType,
8100
+ defaultSourceChainId,
8101
+ initialSelectionDone
8102
+ ]);
8103
+ (0, import_react13.useEffect)(() => {
8104
+ if (!supportedTokens.length || !token) return;
8105
+ const currentToken = supportedTokens.find((t11) => t11.symbol === token);
8106
+ if (!currentToken || currentToken.chains.length === 0) return;
8107
+ const isChainAvailable = chain && currentToken.chains.some((c) => {
8108
+ return getChainKey(c.chain_id, c.chain_type) === chain;
8109
+ });
8110
+ if (!isChainAvailable) {
8111
+ const firstChain = currentToken.chains[0];
8112
+ setChain(getChainKey(firstChain.chain_id, firstChain.chain_type));
8113
+ }
8114
+ }, [token, supportedTokens, chain]);
8115
+ return { token, chain, setToken, setChain, initialSelectionDone };
8116
+ }
8117
+
7975
8118
  // src/components/deposits/shared/DepositFooterLinks.tsx
7976
8119
  var import_jsx_runtime38 = require("react/jsx-runtime");
7977
8120
  function DepositFooterLinks({
@@ -8138,9 +8281,9 @@ function GlossaryModal({
8138
8281
  }
8139
8282
 
8140
8283
  // src/components/deposits/shared/useCopyAddress.ts
8141
- var import_react13 = require("react");
8284
+ var import_react14 = require("react");
8142
8285
  function useCopyAddress() {
8143
- const [copied, setCopied] = (0, import_react13.useState)(false);
8286
+ const [copied, setCopied] = (0, import_react14.useState)(false);
8144
8287
  const handleCopy = (address) => {
8145
8288
  if (!address) return;
8146
8289
  navigator.clipboard.writeText(address);
@@ -8219,7 +8362,7 @@ TooltipContent.displayName = TooltipPrimitive.Content.displayName;
8219
8362
  var import_core20 = require("@unifold/core");
8220
8363
  var import_jsx_runtime41 = require("react/jsx-runtime");
8221
8364
  var t4 = i18n.transferCrypto;
8222
- var getChainKey = (chainId, chainType) => {
8365
+ var getChainKey2 = (chainId, chainType) => {
8223
8366
  return `${chainType}:${chainId}`;
8224
8367
  };
8225
8368
  var parseChainKey = (chainKey) => {
@@ -8233,6 +8376,10 @@ function TransferCryptoSingleInput({
8233
8376
  destinationChainType,
8234
8377
  destinationChainId,
8235
8378
  destinationTokenAddress,
8379
+ defaultSourceChainType,
8380
+ defaultSourceChainId,
8381
+ defaultSourceTokenAddress,
8382
+ defaultSourceSymbol,
8236
8383
  depositConfirmationMode = "auto_ui",
8237
8384
  onExecutionsChange,
8238
8385
  onDepositSuccess,
@@ -8241,21 +8388,25 @@ function TransferCryptoSingleInput({
8241
8388
  }) {
8242
8389
  const { themeClass, colors: colors2, fonts, components } = useTheme();
8243
8390
  const isDarkMode = themeClass.includes("uf-dark");
8244
- const [token, setToken] = (0, import_react14.useState)("USDC");
8245
- const [chain, setChain] = (0, import_react14.useState)("solana:mainnet");
8246
- const [copied, setCopied] = (0, import_react14.useState)(false);
8391
+ const [copied, setCopied] = (0, import_react15.useState)(false);
8247
8392
  const { copied: copiedRecipient, handleCopy: handleCopyRecipientAddress } = useCopyAddress();
8248
- const [glossaryOpen, setGlossaryOpen] = (0, import_react14.useState)(false);
8249
- const [detailsExpanded, setDetailsExpanded] = (0, import_react14.useState)(false);
8250
- const [depositsModalOpen, setDepositsModalOpen] = (0, import_react14.useState)(false);
8251
- const [tokenSelectorOpen, setTokenSelectorOpen] = (0, import_react14.useState)(false);
8252
- const [initialSelectionDone, setInitialSelectionDone] = (0, import_react14.useState)(false);
8393
+ const [glossaryOpen, setGlossaryOpen] = (0, import_react15.useState)(false);
8394
+ const [detailsExpanded, setDetailsExpanded] = (0, import_react15.useState)(false);
8395
+ const [depositsModalOpen, setDepositsModalOpen] = (0, import_react15.useState)(false);
8396
+ const [tokenSelectorOpen, setTokenSelectorOpen] = (0, import_react15.useState)(false);
8253
8397
  const { data: tokensResponse, isLoading: tokensLoading } = useSupportedDepositTokens(publishableKey, {
8254
8398
  destination_token_address: destinationTokenAddress,
8255
8399
  destination_chain_id: destinationChainId,
8256
8400
  destination_chain_type: destinationChainType
8257
8401
  });
8258
8402
  const supportedTokens = tokensResponse?.data ?? [];
8403
+ const { token, chain, setToken, setChain, initialSelectionDone } = useDefaultSourceToken({
8404
+ supportedTokens,
8405
+ defaultSourceChainType,
8406
+ defaultSourceChainId,
8407
+ defaultSourceTokenAddress,
8408
+ defaultSourceSymbol
8409
+ });
8259
8410
  const {
8260
8411
  data: depositAddressResponse,
8261
8412
  isLoading: walletsLoading,
@@ -8273,10 +8424,10 @@ function TransferCryptoSingleInput({
8273
8424
  const wallets = externalWallets?.length ? externalWallets : depositAddressResponse?.data ?? [];
8274
8425
  const loading = externalWallets?.length ? false : walletsLoading;
8275
8426
  const error = walletsError?.message ?? null;
8276
- const allAvailableChains = (0, import_react14.useMemo)(() => {
8427
+ const allAvailableChains = (0, import_react15.useMemo)(() => {
8277
8428
  const chainsMap = /* @__PURE__ */ new Map();
8278
- supportedTokens.forEach((t7) => {
8279
- t7.chains.forEach((c) => {
8429
+ supportedTokens.forEach((t11) => {
8430
+ t11.chains.forEach((c) => {
8280
8431
  const comboKey = `${c.chain_type}:${c.chain_id}`;
8281
8432
  if (!chainsMap.has(comboKey)) {
8282
8433
  chainsMap.set(comboKey, c);
@@ -8285,10 +8436,10 @@ function TransferCryptoSingleInput({
8285
8436
  });
8286
8437
  return Array.from(chainsMap.values());
8287
8438
  }, [supportedTokens]);
8288
- const currentChainCombo = parseChainKey(chain);
8289
- const currentChainData = allAvailableChains.find(
8439
+ const currentChainCombo = chain ? parseChainKey(chain) : null;
8440
+ const currentChainData = currentChainCombo ? allAvailableChains.find(
8290
8441
  (c) => c.chain_type === currentChainCombo.chainType && c.chain_id === currentChainCombo.chainId
8291
- );
8442
+ ) : void 0;
8292
8443
  const currentChainType = currentChainData?.chain_type || "ethereum";
8293
8444
  const currentWallet = (0, import_core20.getWalletByChainType)(wallets, currentChainType);
8294
8445
  const depositAddress = currentWallet?.address || "";
@@ -8306,84 +8457,26 @@ function TransferCryptoSingleInput({
8306
8457
  onDepositSuccess,
8307
8458
  onDepositError
8308
8459
  });
8309
- (0, import_react14.useEffect)(() => {
8310
- if (!supportedTokens.length || initialSelectionDone) return;
8311
- let selectedTokenData;
8312
- let selectedChainData;
8313
- if (destinationTokenAddress) {
8314
- for (const t7 of supportedTokens) {
8315
- const matchingChain = t7.chains.find(
8316
- (c) => c.token_address.toLowerCase() === destinationTokenAddress.toLowerCase()
8317
- );
8318
- if (matchingChain) {
8319
- selectedTokenData = t7;
8320
- selectedChainData = matchingChain;
8321
- break;
8322
- }
8323
- }
8324
- }
8325
- if (!selectedTokenData) {
8326
- selectedTokenData = supportedTokens.find((t7) => t7.symbol === "USDC");
8327
- if (selectedTokenData && selectedTokenData.chains.length > 0) {
8328
- selectedChainData = selectedTokenData.chains[0];
8329
- }
8330
- }
8331
- if (!selectedTokenData) {
8332
- selectedTokenData = supportedTokens.find((t7) => t7.symbol === "USDT");
8333
- if (selectedTokenData && selectedTokenData.chains.length > 0) {
8334
- selectedChainData = selectedTokenData.chains[0];
8335
- }
8336
- }
8337
- if (!selectedTokenData) {
8338
- selectedTokenData = supportedTokens[0];
8339
- if (selectedTokenData.chains.length > 0) {
8340
- selectedChainData = selectedTokenData.chains[0];
8341
- }
8342
- }
8343
- if (selectedTokenData) {
8344
- setToken(selectedTokenData.symbol);
8345
- }
8346
- if (selectedChainData) {
8347
- setChain(
8348
- getChainKey(selectedChainData.chain_id, selectedChainData.chain_type)
8349
- );
8350
- }
8351
- setInitialSelectionDone(true);
8352
- }, [supportedTokens, destinationTokenAddress, initialSelectionDone]);
8353
- (0, import_react14.useEffect)(() => {
8460
+ (0, import_react15.useEffect)(() => {
8354
8461
  if (onExecutionsChange) {
8355
8462
  onExecutionsChange(depositExecutions);
8356
8463
  }
8357
8464
  }, [depositExecutions, onExecutionsChange]);
8358
- (0, import_react14.useEffect)(() => {
8359
- if (!supportedTokens.length) return;
8360
- const currentToken = supportedTokens.find((t7) => t7.symbol === token);
8361
- if (!currentToken || currentToken.chains.length === 0) return;
8362
- const isChainAvailable = currentToken.chains.some((c) => {
8363
- const key = getChainKey(c.chain_id, c.chain_type);
8364
- return key === chain;
8365
- });
8366
- if (!isChainAvailable) {
8367
- const firstChain = currentToken.chains[0];
8368
- const newChain = getChainKey(firstChain.chain_id, firstChain.chain_type);
8369
- setChain(newChain);
8370
- }
8371
- }, [token, supportedTokens, chain]);
8372
- const selectedToken = supportedTokens.find((t7) => t7.symbol === token);
8465
+ const selectedToken = token ? supportedTokens.find((t11) => t11.symbol === token) : void 0;
8373
8466
  const availableChainsForToken = selectedToken?.chains || [];
8374
- const currentChainFromBackend = availableChainsForToken.find((c) => {
8375
- const key = getChainKey(c.chain_id, c.chain_type);
8467
+ const currentChainFromBackend = chain ? availableChainsForToken.find((c) => {
8468
+ const key = getChainKey2(c.chain_id, c.chain_type);
8376
8469
  return key === chain;
8377
8470
  }) || allAvailableChains.find((c) => {
8378
- const key = getChainKey(c.chain_id, c.chain_type);
8471
+ const key = getChainKey2(c.chain_id, c.chain_type);
8379
8472
  return key === chain;
8380
- });
8473
+ }) : void 0;
8381
8474
  const handleCopyAddress = () => {
8382
8475
  navigator.clipboard.writeText(depositAddress);
8383
8476
  setCopied(true);
8384
8477
  setTimeout(() => setCopied(false), 2e3);
8385
8478
  };
8386
- const formatProcessingTime2 = (seconds) => {
8479
+ const formatProcessingTime3 = (seconds) => {
8387
8480
  if (seconds === null) {
8388
8481
  return t4.processingTime.lessThanMinutes.replace("{{minutes}}", "1");
8389
8482
  }
@@ -8412,10 +8505,10 @@ function TransferCryptoSingleInput({
8412
8505
  "button",
8413
8506
  {
8414
8507
  onClick: () => setTokenSelectorOpen(true),
8415
- disabled: tokensLoading || supportedTokens.length === 0,
8508
+ disabled: tokensLoading || !token || supportedTokens.length === 0,
8416
8509
  className: "uf-w-full hover:uf-bg-accent uf-p-3 uf-flex uf-items-center uf-gap-3 uf-transition-colors disabled:uf-opacity-50 disabled:uf-cursor-not-allowed",
8417
8510
  style: { backgroundColor: components.card.backgroundColor, borderRadius: components.card.borderRadius, border: `${components.card.borderWidth}px solid ${components.card.borderColor}` },
8418
- children: tokensLoading ? /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "uf-flex uf-items-center uf-gap-3 uf-animate-pulse", children: [
8511
+ children: tokensLoading || !token ? /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "uf-flex uf-items-center uf-gap-3 uf-animate-pulse", children: [
8419
8512
  /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("div", { className: "uf-w-10 uf-h-10 uf-rounded-full uf-bg-muted" }),
8420
8513
  /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "uf-flex-1", children: [
8421
8514
  /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("div", { className: "uf-h-4 uf-w-16 uf-bg-muted uf-rounded uf-mb-1" }),
@@ -8489,8 +8582,8 @@ function TransferCryptoSingleInput({
8489
8582
  open: tokenSelectorOpen,
8490
8583
  onOpenChange: setTokenSelectorOpen,
8491
8584
  tokens: supportedTokens,
8492
- selectedToken: token,
8493
- selectedChainKey: chain,
8585
+ selectedToken: token ?? "",
8586
+ selectedChainKey: chain ?? "",
8494
8587
  onSelect: (newToken, newChain) => {
8495
8588
  setToken(newToken);
8496
8589
  setChain(newChain);
@@ -8592,7 +8685,7 @@ function TransferCryptoSingleInput({
8592
8685
  t4.processingTime.label,
8593
8686
  ":",
8594
8687
  " ",
8595
- /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("span", { style: { color: components.card.titleColor, fontFamily: fonts.medium }, children: formatProcessingTime2(processingTime) })
8688
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("span", { style: { color: components.card.titleColor, fontFamily: fonts.medium }, children: formatProcessingTime3(processingTime) })
8596
8689
  ] })
8597
8690
  ] }),
8598
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 } })
@@ -8702,7 +8795,7 @@ function TransferCryptoSingleInput({
8702
8795
  }
8703
8796
 
8704
8797
  // src/components/deposits/TransferCryptoDoubleInput.tsx
8705
- var import_react15 = require("react");
8798
+ var import_react16 = require("react");
8706
8799
  var import_lucide_react18 = require("lucide-react");
8707
8800
 
8708
8801
  // src/components/shared/select.tsx
@@ -8843,7 +8936,7 @@ SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
8843
8936
  var import_core21 = require("@unifold/core");
8844
8937
  var import_jsx_runtime43 = require("react/jsx-runtime");
8845
8938
  var t5 = i18n.transferCrypto;
8846
- var getChainKey2 = (chainId, chainType) => {
8939
+ var getChainKey3 = (chainId, chainType) => {
8847
8940
  return `${chainType}:${chainId}`;
8848
8941
  };
8849
8942
  var parseChainKey2 = (chainKey) => {
@@ -8857,6 +8950,10 @@ function TransferCryptoDoubleInput({
8857
8950
  destinationChainType,
8858
8951
  destinationChainId,
8859
8952
  destinationTokenAddress,
8953
+ defaultSourceChainType,
8954
+ defaultSourceChainId,
8955
+ defaultSourceTokenAddress,
8956
+ defaultSourceSymbol,
8860
8957
  depositConfirmationMode = "auto_ui",
8861
8958
  onExecutionsChange,
8862
8959
  onDepositSuccess,
@@ -8865,20 +8962,24 @@ function TransferCryptoDoubleInput({
8865
8962
  }) {
8866
8963
  const { themeClass, colors: colors2, fonts, components } = useTheme();
8867
8964
  const isDarkMode = themeClass.includes("uf-dark");
8868
- const [token, setToken] = (0, import_react15.useState)("USDC");
8869
- const [chain, setChain] = (0, import_react15.useState)("solana:mainnet");
8870
- const [copied, setCopied] = (0, import_react15.useState)(false);
8965
+ const [copied, setCopied] = (0, import_react16.useState)(false);
8871
8966
  const { copied: copiedRecipient, handleCopy: handleCopyRecipientAddress } = useCopyAddress();
8872
- const [glossaryOpen, setGlossaryOpen] = (0, import_react15.useState)(false);
8873
- const [detailsExpanded, setDetailsExpanded] = (0, import_react15.useState)(false);
8874
- const [depositsModalOpen, setDepositsModalOpen] = (0, import_react15.useState)(false);
8875
- const [initialSelectionDone, setInitialSelectionDone] = (0, import_react15.useState)(false);
8967
+ const [glossaryOpen, setGlossaryOpen] = (0, import_react16.useState)(false);
8968
+ const [detailsExpanded, setDetailsExpanded] = (0, import_react16.useState)(false);
8969
+ const [depositsModalOpen, setDepositsModalOpen] = (0, import_react16.useState)(false);
8876
8970
  const { data: tokensResponse, isLoading: tokensLoading } = useSupportedDepositTokens(publishableKey, {
8877
8971
  destination_token_address: destinationTokenAddress,
8878
8972
  destination_chain_id: destinationChainId,
8879
8973
  destination_chain_type: destinationChainType
8880
8974
  });
8881
8975
  const supportedTokens = tokensResponse?.data ?? [];
8976
+ const { token, chain, setToken, setChain, initialSelectionDone } = useDefaultSourceToken({
8977
+ supportedTokens,
8978
+ defaultSourceChainType,
8979
+ defaultSourceChainId,
8980
+ defaultSourceTokenAddress,
8981
+ defaultSourceSymbol
8982
+ });
8882
8983
  const {
8883
8984
  data: depositAddressResponse,
8884
8985
  isLoading: walletsLoading,
@@ -8896,10 +8997,10 @@ function TransferCryptoDoubleInput({
8896
8997
  const wallets = externalWallets?.length ? externalWallets : depositAddressResponse?.data ?? [];
8897
8998
  const loading = externalWallets?.length ? false : walletsLoading;
8898
8999
  const error = walletsError?.message ?? null;
8899
- const allAvailableChains = (0, import_react15.useMemo)(() => {
9000
+ const allAvailableChains = (0, import_react16.useMemo)(() => {
8900
9001
  const chainsMap = /* @__PURE__ */ new Map();
8901
- supportedTokens.forEach((t7) => {
8902
- t7.chains.forEach((c) => {
9002
+ supportedTokens.forEach((t11) => {
9003
+ t11.chains.forEach((c) => {
8903
9004
  const comboKey = `${c.chain_type}:${c.chain_id}`;
8904
9005
  if (!chainsMap.has(comboKey)) {
8905
9006
  chainsMap.set(comboKey, c);
@@ -8908,10 +9009,10 @@ function TransferCryptoDoubleInput({
8908
9009
  });
8909
9010
  return Array.from(chainsMap.values());
8910
9011
  }, [supportedTokens]);
8911
- const currentChainCombo = parseChainKey2(chain);
8912
- const currentChainData = allAvailableChains.find(
9012
+ const currentChainCombo = chain ? parseChainKey2(chain) : null;
9013
+ const currentChainData = currentChainCombo ? allAvailableChains.find(
8913
9014
  (c) => c.chain_type === currentChainCombo.chainType && c.chain_id === currentChainCombo.chainId
8914
- );
9015
+ ) : void 0;
8915
9016
  const currentChainType = currentChainData?.chain_type || "ethereum";
8916
9017
  const currentWallet = (0, import_core21.getWalletByChainType)(wallets, currentChainType);
8917
9018
  const depositAddress = currentWallet?.address || "";
@@ -8929,57 +9030,26 @@ function TransferCryptoDoubleInput({
8929
9030
  onDepositSuccess,
8930
9031
  onDepositError
8931
9032
  });
8932
- (0, import_react15.useEffect)(() => {
8933
- if (!supportedTokens.length || initialSelectionDone) return;
8934
- const allChains = /* @__PURE__ */ new Set();
8935
- supportedTokens.forEach((t7) => {
8936
- t7.chains.forEach((c) => {
8937
- allChains.add(getChainKey2(c.chain_id, c.chain_type));
8938
- });
8939
- });
8940
- if (!allChains.has(chain)) {
8941
- const firstToken = supportedTokens[0];
8942
- if (firstToken.chains.length > 0) {
8943
- const firstChain = firstToken.chains[0];
8944
- setChain(getChainKey2(firstChain.chain_id, firstChain.chain_type));
8945
- }
8946
- }
8947
- setInitialSelectionDone(true);
8948
- }, [supportedTokens, chain, initialSelectionDone]);
8949
- (0, import_react15.useEffect)(() => {
9033
+ (0, import_react16.useEffect)(() => {
8950
9034
  if (onExecutionsChange) {
8951
9035
  onExecutionsChange(depositExecutions);
8952
9036
  }
8953
9037
  }, [depositExecutions, onExecutionsChange]);
8954
- (0, import_react15.useEffect)(() => {
8955
- if (!supportedTokens.length) return;
8956
- const currentToken = supportedTokens.find((t7) => t7.symbol === token);
8957
- if (!currentToken || currentToken.chains.length === 0) return;
8958
- const isChainAvailable = currentToken.chains.some((c) => {
8959
- const key = getChainKey2(c.chain_id, c.chain_type);
8960
- return key === chain;
8961
- });
8962
- if (!isChainAvailable) {
8963
- const firstChain = currentToken.chains[0];
8964
- const newChain = getChainKey2(firstChain.chain_id, firstChain.chain_type);
8965
- setChain(newChain);
8966
- }
8967
- }, [token, supportedTokens, chain]);
8968
- const selectedToken = supportedTokens.find((t7) => t7.symbol === token);
9038
+ const selectedToken = token ? supportedTokens.find((t11) => t11.symbol === token) : void 0;
8969
9039
  const availableChainsForToken = selectedToken?.chains || [];
8970
- const currentChainFromBackend = availableChainsForToken.find((c) => {
8971
- const key = getChainKey2(c.chain_id, c.chain_type);
9040
+ const currentChainFromBackend = chain ? availableChainsForToken.find((c) => {
9041
+ const key = getChainKey3(c.chain_id, c.chain_type);
8972
9042
  return key === chain;
8973
9043
  }) || allAvailableChains.find((c) => {
8974
- const key = getChainKey2(c.chain_id, c.chain_type);
9044
+ const key = getChainKey3(c.chain_id, c.chain_type);
8975
9045
  return key === chain;
8976
- });
9046
+ }) : void 0;
8977
9047
  const handleCopyAddress = () => {
8978
9048
  navigator.clipboard.writeText(depositAddress);
8979
9049
  setCopied(true);
8980
9050
  setTimeout(() => setCopied(false), 2e3);
8981
9051
  };
8982
- const formatProcessingTime2 = (seconds) => {
9052
+ const formatProcessingTime3 = (seconds) => {
8983
9053
  if (seconds === null) {
8984
9054
  return t5.processingTime.lessThanMinutes.replace("{{minutes}}", "1");
8985
9055
  }
@@ -9047,11 +9117,11 @@ function TransferCryptoDoubleInput({
9047
9117
  /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(
9048
9118
  Select,
9049
9119
  {
9050
- value: token,
9120
+ value: token ?? "",
9051
9121
  onValueChange: setToken,
9052
- disabled: tokensLoading || supportedTokens.length === 0,
9122
+ disabled: tokensLoading || !token || supportedTokens.length === 0,
9053
9123
  children: [
9054
- /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(SelectTrigger, { className: "uf-h-10 hover:uf-opacity-90 uf-text-foreground disabled:uf-opacity-50", style: { backgroundColor: components.card.backgroundColor, border: `${components.card.borderWidth}px solid ${components.card.borderColor}` }, children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(SelectValue, { children: tokensLoading ? /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("div", { className: "uf-flex uf-items-center uf-gap-2", children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("span", { className: "uf-text-xs uf-font-light uf-text-muted-foreground", children: t5.loading }) }) : selectedToken ? renderTokenItem(selectedToken) : /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("div", { className: "uf-flex uf-items-center uf-gap-2", children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("span", { className: "uf-text-xs uf-font-normal", children: token }) }) }) }),
9124
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(SelectTrigger, { className: "uf-h-10 hover:uf-opacity-90 uf-text-foreground disabled:uf-opacity-50", style: { backgroundColor: components.card.backgroundColor, border: `${components.card.borderWidth}px solid ${components.card.borderColor}` }, children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(SelectValue, { children: tokensLoading || !token ? /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("div", { className: "uf-flex uf-items-center uf-gap-2", children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("span", { className: "uf-text-xs uf-font-light uf-text-muted-foreground", children: t5.loading }) }) : selectedToken ? renderTokenItem(selectedToken) : /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("div", { className: "uf-flex uf-items-center uf-gap-2", children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("span", { className: "uf-text-xs uf-font-normal", children: token }) }) }) }),
9055
9125
  /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(SelectContent, { className: "uf-bg-secondary uf-border uf-text-foreground uf-max-h-[300px]", style: { border: `1px solid ${isDarkMode ? "rgba(255,255,255,0.15)" : "rgba(0,0,0,0.15)"}`, ...fonts.regular ? { "--uf-font-family": fonts.regular } : {} }, children: supportedTokens.map((tokenData) => /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
9056
9126
  SelectItem,
9057
9127
  {
@@ -9078,11 +9148,11 @@ function TransferCryptoDoubleInput({
9078
9148
  /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(
9079
9149
  Select,
9080
9150
  {
9081
- value: chain,
9151
+ value: chain ?? "",
9082
9152
  onValueChange: setChain,
9083
- disabled: tokensLoading || availableChainsForToken.length === 0,
9153
+ disabled: tokensLoading || !chain || availableChainsForToken.length === 0,
9084
9154
  children: [
9085
- /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(SelectTrigger, { className: "uf-h-10 hover:uf-opacity-90 uf-text-foreground disabled:uf-opacity-50", style: { backgroundColor: components.card.backgroundColor, border: `${components.card.borderWidth}px solid ${components.card.borderColor}` }, children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(SelectValue, { children: tokensLoading ? /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("div", { className: "uf-flex uf-items-center uf-gap-2", children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("span", { className: "uf-text-xs uf-font-light uf-text-muted-foreground", children: t5.loading }) }) : currentChainFromBackend ? renderChainItem(currentChainFromBackend) : currentChainData ? renderChainItem(currentChainData) : /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("div", { className: "uf-flex uf-items-center uf-gap-2", children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("span", { className: "uf-text-xs uf-font-normal", children: chain }) }) }) }),
9155
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(SelectTrigger, { className: "uf-h-10 hover:uf-opacity-90 uf-text-foreground disabled:uf-opacity-50", style: { backgroundColor: components.card.backgroundColor, border: `${components.card.borderWidth}px solid ${components.card.borderColor}` }, children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(SelectValue, { children: tokensLoading || !chain ? /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("div", { className: "uf-flex uf-items-center uf-gap-2", children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("span", { className: "uf-text-xs uf-font-light uf-text-muted-foreground", children: t5.loading }) }) : currentChainFromBackend ? renderChainItem(currentChainFromBackend) : currentChainData ? renderChainItem(currentChainData) : /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("div", { className: "uf-flex uf-items-center uf-gap-2", children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("span", { className: "uf-text-xs uf-font-normal", children: chain }) }) }) }),
9086
9156
  /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
9087
9157
  SelectContent,
9088
9158
  {
@@ -9090,7 +9160,7 @@ function TransferCryptoDoubleInput({
9090
9160
  className: "uf-bg-secondary uf-border uf-text-foreground uf-max-h-[300px] uf-min-w-[200px]",
9091
9161
  style: { border: `1px solid ${isDarkMode ? "rgba(255,255,255,0.15)" : "rgba(0,0,0,0.15)"}`, ...fonts.regular ? { "--uf-font-family": fonts.regular } : {} },
9092
9162
  children: availableChainsForToken.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("div", { className: "uf-px-2 uf-py-3 uf-text-xs uf-text-muted-foreground uf-text-center", children: t5.noChainsAvailable }) : availableChainsForToken.map((chainData) => {
9093
- const chainKey = getChainKey2(
9163
+ const chainKey = getChainKey3(
9094
9164
  chainData.chain_id,
9095
9165
  chainData.chain_type
9096
9166
  );
@@ -9206,7 +9276,7 @@ function TransferCryptoDoubleInput({
9206
9276
  t5.processingTime.label,
9207
9277
  ":",
9208
9278
  " ",
9209
- /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("span", { style: { color: components.card.titleColor, fontFamily: fonts.medium }, children: formatProcessingTime2(processingTime) })
9279
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("span", { style: { color: components.card.titleColor, fontFamily: fonts.medium }, children: formatProcessingTime3(processingTime) })
9210
9280
  ] })
9211
9281
  ] }),
9212
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 } })
@@ -10102,7 +10172,7 @@ function ReviewView({
10102
10172
  }
10103
10173
 
10104
10174
  // src/components/deposits/browser-wallets/ConfirmingView.tsx
10105
- var import_react16 = require("react");
10175
+ var import_react17 = require("react");
10106
10176
  var import_lucide_react21 = require("lucide-react");
10107
10177
  var import_jsx_runtime48 = require("react/jsx-runtime");
10108
10178
  function ConfirmingView({
@@ -10112,8 +10182,8 @@ function ConfirmingView({
10112
10182
  isPolling = false
10113
10183
  }) {
10114
10184
  const { colors: colors2, fonts } = useTheme();
10115
- const [containerEl, setContainerEl] = (0, import_react16.useState)(null);
10116
- const containerCallbackRef = (0, import_react16.useCallback)((el) => {
10185
+ const [containerEl, setContainerEl] = (0, import_react17.useState)(null);
10186
+ const containerCallbackRef = (0, import_react17.useCallback)((el) => {
10117
10187
  setContainerEl(el);
10118
10188
  }, []);
10119
10189
  return /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(PortalContainerProvider, { value: containerEl, children: /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)(
@@ -10280,7 +10350,7 @@ function BrowserWalletModal({
10280
10350
  );
10281
10351
  if (cancelled) return;
10282
10352
  const supportedToken = response.data.find(
10283
- (t7) => t7.symbol.toLowerCase() === token.symbol.toLowerCase()
10353
+ (t11) => t11.symbol.toLowerCase() === token.symbol.toLowerCase()
10284
10354
  );
10285
10355
  if (supportedToken) {
10286
10356
  const chainDetail = supportedToken.chains.find(
@@ -11496,6 +11566,10 @@ function DepositModal({
11496
11566
  destinationChainType,
11497
11567
  destinationChainId,
11498
11568
  destinationTokenAddress,
11569
+ defaultSourceChainType,
11570
+ defaultSourceChainId,
11571
+ defaultSourceTokenAddress,
11572
+ defaultSourceSymbol,
11499
11573
  hideDepositTracker = false,
11500
11574
  showBalanceHeader = false,
11501
11575
  transferInputVariant = "double_input",
@@ -11517,33 +11591,33 @@ function DepositModal({
11517
11591
  depositTrackerSubTitle = t6.depositTracker.subtitle
11518
11592
  }) {
11519
11593
  const { colors: colors2, fonts, components } = useTheme();
11520
- const effectiveInitialScreen = (0, import_react17.useMemo)(() => {
11594
+ const effectiveInitialScreen = (0, import_react18.useMemo)(() => {
11521
11595
  const s = initialScreen ?? "main";
11522
11596
  if (s === "tracker" && hideDepositTracker) return "main";
11523
11597
  return s;
11524
11598
  }, [initialScreen, hideDepositTracker]);
11525
- const [containerEl, setContainerEl] = (0, import_react17.useState)(null);
11526
- const containerCallbackRef = (0, import_react17.useCallback)((el) => {
11599
+ const [containerEl, setContainerEl] = (0, import_react18.useState)(null);
11600
+ const containerCallbackRef = (0, import_react18.useCallback)((el) => {
11527
11601
  setContainerEl(el);
11528
11602
  }, []);
11529
- const [view, setView] = (0, import_react17.useState)(
11603
+ const [view, setView] = (0, import_react18.useState)(
11530
11604
  effectiveInitialScreen
11531
11605
  );
11532
- const resetViewTimeoutRef = (0, import_react17.useRef)(null);
11533
- const [cardView, setCardView] = (0, import_react17.useState)(
11606
+ const resetViewTimeoutRef = (0, import_react18.useRef)(null);
11607
+ const [cardView, setCardView] = (0, import_react18.useState)(
11534
11608
  "amount"
11535
11609
  );
11536
- const [exchangeView, setExchangeView] = (0, import_react17.useState)(
11610
+ const [exchangeView, setExchangeView] = (0, import_react18.useState)(
11537
11611
  "providers"
11538
11612
  );
11539
- const [browserWalletModalOpen, setBrowserWalletModalOpen] = (0, import_react17.useState)(false);
11540
- const [browserWalletInfo, setBrowserWalletInfo] = (0, import_react17.useState)(null);
11541
- const [walletSelectionModalOpen, setWalletSelectionModalOpen] = (0, import_react17.useState)(false);
11542
- const [browserWalletChainType, setBrowserWalletChainType] = (0, import_react17.useState)(() => getStoredWalletChainType());
11543
- const [quotesCount, setQuotesCount] = (0, import_react17.useState)(0);
11544
- const [allExecutions, setAllExecutions] = (0, import_react17.useState)([]);
11545
- const [selectedExecution, setSelectedExecution] = (0, import_react17.useState)(null);
11546
- const [depositExecutions, setDepositExecutions] = (0, import_react17.useState)([]);
11613
+ const [browserWalletModalOpen, setBrowserWalletModalOpen] = (0, import_react18.useState)(false);
11614
+ const [browserWalletInfo, setBrowserWalletInfo] = (0, import_react18.useState)(null);
11615
+ const [walletSelectionModalOpen, setWalletSelectionModalOpen] = (0, import_react18.useState)(false);
11616
+ const [browserWalletChainType, setBrowserWalletChainType] = (0, import_react18.useState)(() => getStoredWalletChainType());
11617
+ const [quotesCount, setQuotesCount] = (0, import_react18.useState)(0);
11618
+ const [allExecutions, setAllExecutions] = (0, import_react18.useState)([]);
11619
+ const [selectedExecution, setSelectedExecution] = (0, import_react18.useState)(null);
11620
+ const [depositExecutions, setDepositExecutions] = (0, import_react18.useState)([]);
11547
11621
  const isMobileView = useIsMobileViewport();
11548
11622
  const { data: depositAddressResponse, isLoading: walletsLoading } = useDepositAddress({
11549
11623
  userId,
@@ -11556,10 +11630,10 @@ function DepositModal({
11556
11630
  // Only fetch when modal is open
11557
11631
  });
11558
11632
  const wallets = depositAddressResponse?.data ?? [];
11559
- const [resolvedTheme, setResolvedTheme] = (0, import_react17.useState)(
11633
+ const [resolvedTheme, setResolvedTheme] = (0, import_react18.useState)(
11560
11634
  theme === "auto" ? "dark" : theme
11561
11635
  );
11562
- (0, import_react17.useEffect)(() => {
11636
+ (0, import_react18.useEffect)(() => {
11563
11637
  if (theme === "auto") {
11564
11638
  const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
11565
11639
  setResolvedTheme(mediaQuery.matches ? "dark" : "light");
@@ -11588,11 +11662,11 @@ function DepositModal({
11588
11662
  chainType: destinationChainType,
11589
11663
  enabled: open
11590
11664
  });
11591
- (0, import_react17.useEffect)(() => {
11665
+ (0, import_react18.useEffect)(() => {
11592
11666
  if (view !== "tracker" || !userId) return;
11593
11667
  const fetchExecutions = async () => {
11594
11668
  try {
11595
- const response = await (0, import_core23.queryExecutions)(userId, publishableKey);
11669
+ const response = await (0, import_core23.queryExecutions)(userId, publishableKey, import_core23.ActionType.Deposit);
11596
11670
  const sorted = [...response.data].sort((a, b) => {
11597
11671
  const timeA = a.created_at ? new Date(a.created_at).getTime() : 0;
11598
11672
  const timeB = b.created_at ? new Date(b.created_at).getTime() : 0;
@@ -11609,7 +11683,7 @@ function DepositModal({
11609
11683
  clearInterval(pollInterval);
11610
11684
  };
11611
11685
  }, [view, userId, publishableKey]);
11612
- (0, import_react17.useEffect)(() => {
11686
+ (0, import_react18.useEffect)(() => {
11613
11687
  if (view !== "tracker") {
11614
11688
  setSelectedExecution(null);
11615
11689
  }
@@ -11707,7 +11781,7 @@ function DepositModal({
11707
11781
  resetViewTimeoutRef.current = null;
11708
11782
  }, 200);
11709
11783
  };
11710
- (0, import_react17.useLayoutEffect)(() => {
11784
+ (0, import_react18.useLayoutEffect)(() => {
11711
11785
  if (!open) return;
11712
11786
  if (resetViewTimeoutRef.current) {
11713
11787
  clearTimeout(resetViewTimeoutRef.current);
@@ -11719,7 +11793,7 @@ function DepositModal({
11719
11793
  setBrowserWalletInfo(null);
11720
11794
  setSelectedExecution(null);
11721
11795
  }, [open, effectiveInitialScreen]);
11722
- (0, import_react17.useEffect)(
11796
+ (0, import_react18.useEffect)(
11723
11797
  () => () => {
11724
11798
  if (resetViewTimeoutRef.current) {
11725
11799
  clearTimeout(resetViewTimeoutRef.current);
@@ -11928,6 +12002,10 @@ function DepositModal({
11928
12002
  destinationChainType,
11929
12003
  destinationChainId,
11930
12004
  destinationTokenAddress,
12005
+ defaultSourceChainType,
12006
+ defaultSourceChainId,
12007
+ defaultSourceTokenAddress,
12008
+ defaultSourceSymbol,
11931
12009
  depositConfirmationMode,
11932
12010
  onExecutionsChange: setDepositExecutions,
11933
12011
  onDepositSuccess,
@@ -11943,6 +12021,10 @@ function DepositModal({
11943
12021
  destinationChainType,
11944
12022
  destinationChainId,
11945
12023
  destinationTokenAddress,
12024
+ defaultSourceChainType,
12025
+ defaultSourceChainId,
12026
+ defaultSourceTokenAddress,
12027
+ defaultSourceSymbol,
11946
12028
  depositConfirmationMode,
11947
12029
  onExecutionsChange: setDepositExecutions,
11948
12030
  onDepositSuccess,
@@ -12103,6 +12185,1977 @@ function DepositModal({
12103
12185
  }
12104
12186
  ) });
12105
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
+ }
12106
14159
  // Annotate the CommonJS export names for ESM import in node:
12107
14160
  0 && (module.exports = {
12108
14161
  Button,
@@ -12149,15 +14202,29 @@ function DepositModal({
12149
14202
  TransferCryptoButton,
12150
14203
  TransferCryptoDoubleInput,
12151
14204
  TransferCryptoSingleInput,
14205
+ WithdrawConfirmingView,
14206
+ WithdrawDoubleInput,
14207
+ WithdrawExecutionItem,
14208
+ WithdrawForm,
14209
+ WithdrawModal,
14210
+ WithdrawTokenSelector,
12152
14211
  buttonVariants,
12153
14212
  cn,
12154
14213
  colors,
12155
14214
  defaultColors,
14215
+ detectBrowserWallet,
12156
14216
  getColors,
12157
14217
  mergeColors,
12158
14218
  resolveComponentTokens,
14219
+ sendEvmWithdraw,
14220
+ sendSolanaWithdraw,
12159
14221
  truncateAddress,
14222
+ useAddressBalance,
12160
14223
  useAllowedCountry,
12161
14224
  useDepositPolling,
12162
- useTheme
14225
+ useSourceTokenValidation,
14226
+ useSupportedDestinationTokens,
14227
+ useTheme,
14228
+ useVerifyRecipientAddress,
14229
+ useWithdrawPolling
12163
14230
  });