@unifold/ui-react 0.1.41 → 0.1.42

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -621,6 +621,7 @@ function useDepositAddress(params) {
621
621
  destinationChainType,
622
622
  destinationChainId,
623
623
  destinationTokenAddress,
624
+ actionType,
624
625
  enabled = true
625
626
  } = params;
626
627
  return useQuery({
@@ -632,6 +633,7 @@ function useDepositAddress(params) {
632
633
  destinationChainType ?? null,
633
634
  destinationChainId ?? null,
634
635
  destinationTokenAddress ?? null,
636
+ actionType ?? null,
635
637
  publishableKey
636
638
  ],
637
639
  queryFn: () => createDepositAddress(
@@ -640,7 +642,8 @@ function useDepositAddress(params) {
640
642
  recipient_address: recipientAddress,
641
643
  destination_chain_type: destinationChainType,
642
644
  destination_chain_id: destinationChainId,
643
- destination_token_address: destinationTokenAddress
645
+ destination_token_address: destinationTokenAddress,
646
+ action_type: actionType
644
647
  },
645
648
  publishableKey
646
649
  ),
@@ -1354,6 +1357,30 @@ var en_default = {
1354
1357
  youReceive: "You receive",
1355
1358
  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."
1356
1359
  }
1360
+ },
1361
+ withdrawModal: {
1362
+ title: "Withdraw",
1363
+ withdrawCrypto: {
1364
+ title: "Withdraw with Crypto",
1365
+ subtitle: "Send to any wallet address"
1366
+ },
1367
+ selectToken: "Select withdrawal token",
1368
+ receiveToken: "Receive token",
1369
+ receiveChain: "Receive chain",
1370
+ recipientAddress: "Recipient address",
1371
+ recipientAddressPlaceholder: "Enter wallet address",
1372
+ amount: "Amount",
1373
+ amountPlaceholder: "0.00",
1374
+ balance: "Balance",
1375
+ minimum: "min",
1376
+ withdraw: "Withdraw",
1377
+ invalidAddress: "Please enter a valid address",
1378
+ invalidAmount: "Please enter a valid amount",
1379
+ verifyingAddress: "Verifying address...",
1380
+ loading: "Loading...",
1381
+ noTokensAvailable: "No tokens available",
1382
+ review: "Review Withdrawal",
1383
+ back: "Back"
1357
1384
  }
1358
1385
  };
1359
1386
 
@@ -1372,7 +1399,8 @@ import { useState as useState5, useEffect as useEffect3, useRef } from "react";
1372
1399
  import {
1373
1400
  queryExecutions,
1374
1401
  pollDirectExecutions,
1375
- ExecutionStatus
1402
+ ExecutionStatus,
1403
+ ActionType
1376
1404
  } from "@unifold/core";
1377
1405
  var DEPOSIT_CONFIRM_DELAY_MS = 1e4;
1378
1406
  var POLL_INTERVAL_MS = 2500;
@@ -1442,7 +1470,7 @@ function useDepositPolling({
1442
1470
  const modalOpenedAt = modalOpenedAtRef.current;
1443
1471
  const poll = async () => {
1444
1472
  try {
1445
- const response = await queryExecutions(userId, publishableKey);
1473
+ const response = await queryExecutions(userId, publishableKey, ActionType.Deposit);
1446
1474
  const cutoff = new Date(modalOpenedAt.getTime() - CUTOFF_BUFFER_MS);
1447
1475
  const sortedExecutions = [...response.data].sort((a, b) => {
1448
1476
  const timeA = a.created_at ? new Date(a.created_at).getTime() : 0;
@@ -1586,7 +1614,8 @@ function formatCurrency(currency) {
1586
1614
  }
1587
1615
  function DepositDetailContent({
1588
1616
  execution,
1589
- className
1617
+ className,
1618
+ variant = "deposit"
1590
1619
  }) {
1591
1620
  const { colors: colors2, fonts, components } = useTheme();
1592
1621
  const [chains, setChains] = useState6([]);
@@ -1992,7 +2021,7 @@ function DepositDetailContent({
1992
2021
  color: components.card.rowLeftLabel,
1993
2022
  fontFamily: fonts.regular
1994
2023
  },
1995
- children: "Deposit Tx"
2024
+ children: variant === "withdraw" ? "Withdrawal Tx" : "Deposit Tx"
1996
2025
  }
1997
2026
  ),
1998
2027
  /* @__PURE__ */ jsx7(
@@ -6900,7 +6929,8 @@ function useProjectConfig({
6900
6929
 
6901
6930
  // src/components/deposits/DepositModal.tsx
6902
6931
  import {
6903
- queryExecutions as queryExecutions3
6932
+ queryExecutions as queryExecutions3,
6933
+ ActionType as ActionType3
6904
6934
  } from "@unifold/core";
6905
6935
 
6906
6936
  // src/hooks/use-allowed-country.ts
@@ -7287,7 +7317,7 @@ function PoweredByUnifold({
7287
7317
  }
7288
7318
 
7289
7319
  // src/components/deposits/DepositsModal.tsx
7290
- import { queryExecutions as queryExecutions2 } from "@unifold/core";
7320
+ import { queryExecutions as queryExecutions2, ActionType as ActionType2 } from "@unifold/core";
7291
7321
  import { Fragment as Fragment3, jsx as jsx35, jsxs as jsxs30 } from "react/jsx-runtime";
7292
7322
  function DepositsModal({
7293
7323
  open,
@@ -7305,7 +7335,7 @@ function DepositsModal({
7305
7335
  if (!open || !userId) return;
7306
7336
  const fetchExecutions = async () => {
7307
7337
  try {
7308
- const response = await queryExecutions2(userId, publishableKey);
7338
+ const response = await queryExecutions2(userId, publishableKey, ActionType2.Deposit);
7309
7339
  const sorted = [...response.data].sort((a, b) => {
7310
7340
  const timeA = a.created_at ? new Date(a.created_at).getTime() : 0;
7311
7341
  const timeB = b.created_at ? new Date(b.created_at).getTime() : 0;
@@ -7427,7 +7457,7 @@ function saveRecentToken(token) {
7427
7457
  try {
7428
7458
  const recent = getRecentTokens();
7429
7459
  const filtered = recent.filter(
7430
- (t7) => !(t7.symbol === token.symbol && t7.chainType === token.chainType && t7.chainId === token.chainId)
7460
+ (t11) => !(t11.symbol === token.symbol && t11.chainType === token.chainType && t11.chainId === token.chainId)
7431
7461
  );
7432
7462
  filtered.unshift(token);
7433
7463
  const trimmed = filtered.slice(0, MAX_RECENT_TOKENS);
@@ -7440,7 +7470,7 @@ function removeRecentToken(token) {
7440
7470
  try {
7441
7471
  const recent = getRecentTokens();
7442
7472
  const filtered = recent.filter(
7443
- (t7) => !(t7.symbol === token.symbol && t7.chainType === token.chainType && t7.chainId === token.chainId)
7473
+ (t11) => !(t11.symbol === token.symbol && t11.chainType === token.chainType && t11.chainId === token.chainId)
7444
7474
  );
7445
7475
  localStorage.setItem(STORAGE_KEY, JSON.stringify(filtered));
7446
7476
  return filtered;
@@ -7479,7 +7509,7 @@ function TokenSelectorSheet({
7479
7509
  const addOption = (symbol, chainType, chainId, isRecent) => {
7480
7510
  const key = `${symbol}-${chainType}:${chainId}`;
7481
7511
  if (seen.has(key)) return;
7482
- const tokenData = tokens.find((t7) => t7.symbol === symbol);
7512
+ const tokenData = tokens.find((t11) => t11.symbol === symbol);
7483
7513
  if (!tokenData) return;
7484
7514
  const chainData = tokenData.chains.find(
7485
7515
  (c) => c.chain_type === chainType && c.chain_id === chainId
@@ -7950,35 +7980,35 @@ function resolveSourceToken(supportedTokens, defaultSourceChainType, defaultSour
7950
7980
  let selectedChainData;
7951
7981
  const hasChainDefaults = defaultSourceChainType && defaultSourceChainId;
7952
7982
  if (defaultSourceTokenAddress && hasChainDefaults) {
7953
- for (const t7 of supportedTokens) {
7954
- const matchingChain = t7.chains.find(
7983
+ for (const t11 of supportedTokens) {
7984
+ const matchingChain = t11.chains.find(
7955
7985
  (c) => c.token_address.toLowerCase() === defaultSourceTokenAddress.toLowerCase() && c.chain_type === defaultSourceChainType && c.chain_id === defaultSourceChainId
7956
7986
  );
7957
7987
  if (matchingChain) {
7958
- selectedTokenData = t7;
7988
+ selectedTokenData = t11;
7959
7989
  selectedChainData = matchingChain;
7960
7990
  break;
7961
7991
  }
7962
7992
  }
7963
7993
  }
7964
7994
  if (!selectedTokenData && defaultSourceSymbol && hasChainDefaults) {
7965
- for (const t7 of supportedTokens) {
7966
- if (t7.symbol !== defaultSourceSymbol) continue;
7967
- const matchedChain = t7.chains.find(
7995
+ for (const t11 of supportedTokens) {
7996
+ if (t11.symbol !== defaultSourceSymbol) continue;
7997
+ const matchedChain = t11.chains.find(
7968
7998
  (c) => c.chain_type === defaultSourceChainType && c.chain_id === defaultSourceChainId
7969
7999
  );
7970
8000
  if (matchedChain) {
7971
- selectedTokenData = t7;
8001
+ selectedTokenData = t11;
7972
8002
  selectedChainData = matchedChain;
7973
8003
  break;
7974
8004
  }
7975
8005
  }
7976
8006
  }
7977
8007
  if (!selectedTokenData) {
7978
- for (const t7 of supportedTokens) {
7979
- if (t7.chains.length > 0) {
7980
- selectedTokenData = t7;
7981
- selectedChainData = t7.chains[0];
8008
+ for (const t11 of supportedTokens) {
8009
+ if (t11.chains.length > 0) {
8010
+ selectedTokenData = t11;
8011
+ selectedChainData = t11.chains[0];
7982
8012
  break;
7983
8013
  }
7984
8014
  }
@@ -8027,7 +8057,7 @@ function useDefaultSourceToken({
8027
8057
  ]);
8028
8058
  useEffect16(() => {
8029
8059
  if (!supportedTokens.length || !token) return;
8030
- const currentToken = supportedTokens.find((t7) => t7.symbol === token);
8060
+ const currentToken = supportedTokens.find((t11) => t11.symbol === token);
8031
8061
  if (!currentToken || currentToken.chains.length === 0) return;
8032
8062
  const isChainAvailable = chain && currentToken.chains.some((c) => {
8033
8063
  return getChainKey(c.chain_id, c.chain_type) === chain;
@@ -8353,8 +8383,8 @@ function TransferCryptoSingleInput({
8353
8383
  const error = walletsError?.message ?? null;
8354
8384
  const allAvailableChains = useMemo5(() => {
8355
8385
  const chainsMap = /* @__PURE__ */ new Map();
8356
- supportedTokens.forEach((t7) => {
8357
- t7.chains.forEach((c) => {
8386
+ supportedTokens.forEach((t11) => {
8387
+ t11.chains.forEach((c) => {
8358
8388
  const comboKey = `${c.chain_type}:${c.chain_id}`;
8359
8389
  if (!chainsMap.has(comboKey)) {
8360
8390
  chainsMap.set(comboKey, c);
@@ -8389,7 +8419,7 @@ function TransferCryptoSingleInput({
8389
8419
  onExecutionsChange(depositExecutions);
8390
8420
  }
8391
8421
  }, [depositExecutions, onExecutionsChange]);
8392
- const selectedToken = token ? supportedTokens.find((t7) => t7.symbol === token) : void 0;
8422
+ const selectedToken = token ? supportedTokens.find((t11) => t11.symbol === token) : void 0;
8393
8423
  const availableChainsForToken = selectedToken?.chains || [];
8394
8424
  const currentChainFromBackend = chain ? availableChainsForToken.find((c) => {
8395
8425
  const key = getChainKey2(c.chain_id, c.chain_type);
@@ -8403,7 +8433,7 @@ function TransferCryptoSingleInput({
8403
8433
  setCopied(true);
8404
8434
  setTimeout(() => setCopied(false), 2e3);
8405
8435
  };
8406
- const formatProcessingTime2 = (seconds) => {
8436
+ const formatProcessingTime3 = (seconds) => {
8407
8437
  if (seconds === null) {
8408
8438
  return t4.processingTime.lessThanMinutes.replace("{{minutes}}", "1");
8409
8439
  }
@@ -8612,7 +8642,7 @@ function TransferCryptoSingleInput({
8612
8642
  t4.processingTime.label,
8613
8643
  ":",
8614
8644
  " ",
8615
- /* @__PURE__ */ jsx41("span", { style: { color: components.card.titleColor, fontFamily: fonts.medium }, children: formatProcessingTime2(processingTime) })
8645
+ /* @__PURE__ */ jsx41("span", { style: { color: components.card.titleColor, fontFamily: fonts.medium }, children: formatProcessingTime3(processingTime) })
8616
8646
  ] })
8617
8647
  ] }),
8618
8648
  detailsExpanded ? /* @__PURE__ */ jsx41(ChevronUp2, { className: "uf-w-4 uf-h-4", style: { color: components.card.actionColor } }) : /* @__PURE__ */ jsx41(ChevronDown3, { className: "uf-w-4 uf-h-4", style: { color: components.card.actionColor } })
@@ -8939,8 +8969,8 @@ function TransferCryptoDoubleInput({
8939
8969
  const error = walletsError?.message ?? null;
8940
8970
  const allAvailableChains = useMemo6(() => {
8941
8971
  const chainsMap = /* @__PURE__ */ new Map();
8942
- supportedTokens.forEach((t7) => {
8943
- t7.chains.forEach((c) => {
8972
+ supportedTokens.forEach((t11) => {
8973
+ t11.chains.forEach((c) => {
8944
8974
  const comboKey = `${c.chain_type}:${c.chain_id}`;
8945
8975
  if (!chainsMap.has(comboKey)) {
8946
8976
  chainsMap.set(comboKey, c);
@@ -8975,7 +9005,7 @@ function TransferCryptoDoubleInput({
8975
9005
  onExecutionsChange(depositExecutions);
8976
9006
  }
8977
9007
  }, [depositExecutions, onExecutionsChange]);
8978
- const selectedToken = token ? supportedTokens.find((t7) => t7.symbol === token) : void 0;
9008
+ const selectedToken = token ? supportedTokens.find((t11) => t11.symbol === token) : void 0;
8979
9009
  const availableChainsForToken = selectedToken?.chains || [];
8980
9010
  const currentChainFromBackend = chain ? availableChainsForToken.find((c) => {
8981
9011
  const key = getChainKey3(c.chain_id, c.chain_type);
@@ -8989,7 +9019,7 @@ function TransferCryptoDoubleInput({
8989
9019
  setCopied(true);
8990
9020
  setTimeout(() => setCopied(false), 2e3);
8991
9021
  };
8992
- const formatProcessingTime2 = (seconds) => {
9022
+ const formatProcessingTime3 = (seconds) => {
8993
9023
  if (seconds === null) {
8994
9024
  return t5.processingTime.lessThanMinutes.replace("{{minutes}}", "1");
8995
9025
  }
@@ -9216,7 +9246,7 @@ function TransferCryptoDoubleInput({
9216
9246
  t5.processingTime.label,
9217
9247
  ":",
9218
9248
  " ",
9219
- /* @__PURE__ */ jsx43("span", { style: { color: components.card.titleColor, fontFamily: fonts.medium }, children: formatProcessingTime2(processingTime) })
9249
+ /* @__PURE__ */ jsx43("span", { style: { color: components.card.titleColor, fontFamily: fonts.medium }, children: formatProcessingTime3(processingTime) })
9220
9250
  ] })
9221
9251
  ] }),
9222
9252
  detailsExpanded ? /* @__PURE__ */ jsx43(ChevronUp4, { className: "uf-w-4 uf-h-4", style: { color: components.card.actionColor } }) : /* @__PURE__ */ jsx43(ChevronDown5, { className: "uf-w-4 uf-h-4", style: { color: components.card.actionColor } })
@@ -10295,7 +10325,7 @@ function BrowserWalletModal({
10295
10325
  );
10296
10326
  if (cancelled) return;
10297
10327
  const supportedToken = response.data.find(
10298
- (t7) => t7.symbol.toLowerCase() === token.symbol.toLowerCase()
10328
+ (t11) => t11.symbol.toLowerCase() === token.symbol.toLowerCase()
10299
10329
  );
10300
10330
  if (supportedToken) {
10301
10331
  const chainDetail = supportedToken.chains.find(
@@ -11611,7 +11641,7 @@ function DepositModal({
11611
11641
  if (view !== "tracker" || !userId) return;
11612
11642
  const fetchExecutions = async () => {
11613
11643
  try {
11614
- const response = await queryExecutions3(userId, publishableKey);
11644
+ const response = await queryExecutions3(userId, publishableKey, ActionType3.Deposit);
11615
11645
  const sorted = [...response.data].sort((a, b) => {
11616
11646
  const timeA = a.created_at ? new Date(a.created_at).getTime() : 0;
11617
11647
  const timeB = b.created_at ? new Date(b.created_at).getTime() : 0;
@@ -12130,6 +12160,2011 @@ function DepositModal({
12130
12160
  }
12131
12161
  ) });
12132
12162
  }
12163
+
12164
+ // src/components/withdrawals/WithdrawModal.tsx
12165
+ import {
12166
+ useState as useState31,
12167
+ useEffect as useEffect25,
12168
+ useLayoutEffect as useLayoutEffect3,
12169
+ useCallback as useCallback6,
12170
+ useRef as useRef8
12171
+ } from "react";
12172
+ import { AlertTriangle as AlertTriangle3, ChevronRight as ChevronRight13, Clock as Clock5 } from "lucide-react";
12173
+
12174
+ // src/hooks/use-supported-destination-tokens.ts
12175
+ import { useQuery as useQuery9 } from "@tanstack/react-query";
12176
+ import {
12177
+ getSupportedDestinationTokens
12178
+ } from "@unifold/core";
12179
+ function useSupportedDestinationTokens(publishableKey, enabled = true) {
12180
+ return useQuery9({
12181
+ queryKey: ["unifold", "supportedDestinationTokens", publishableKey],
12182
+ queryFn: () => getSupportedDestinationTokens(publishableKey),
12183
+ staleTime: 1e3 * 60 * 5,
12184
+ gcTime: 1e3 * 60 * 30,
12185
+ refetchOnMount: false,
12186
+ refetchOnWindowFocus: false,
12187
+ enabled
12188
+ });
12189
+ }
12190
+
12191
+ // src/hooks/use-source-token-validation.ts
12192
+ import { useQuery as useQuery10 } from "@tanstack/react-query";
12193
+ import { getSupportedDepositTokens as getSupportedDepositTokens3 } from "@unifold/core";
12194
+ function useSourceTokenValidation(params) {
12195
+ const {
12196
+ sourceChainType,
12197
+ sourceChainId,
12198
+ sourceTokenAddress,
12199
+ sourceTokenSymbol,
12200
+ publishableKey,
12201
+ enabled = true
12202
+ } = params;
12203
+ const hasParams = !!sourceChainType && !!sourceChainId && !!sourceTokenAddress;
12204
+ return useQuery10({
12205
+ queryKey: [
12206
+ "unifold",
12207
+ "sourceTokenValidation",
12208
+ sourceChainType ?? null,
12209
+ sourceChainId ?? null,
12210
+ sourceTokenAddress ?? null,
12211
+ publishableKey
12212
+ ],
12213
+ queryFn: async () => {
12214
+ const res = await getSupportedDepositTokens3(publishableKey);
12215
+ let matchedMinUsd = null;
12216
+ let matchedProcessingTime = null;
12217
+ let matchedSlippage = null;
12218
+ let matchedPriceImpact = null;
12219
+ const found = res.data.some(
12220
+ (token) => token.chains.some((chain) => {
12221
+ const match = chain.chain_type === sourceChainType && chain.chain_id === sourceChainId && chain.token_address.toLowerCase() === sourceTokenAddress.toLowerCase();
12222
+ if (match) {
12223
+ matchedMinUsd = chain.minimum_deposit_amount_usd;
12224
+ matchedProcessingTime = chain.estimated_processing_time;
12225
+ matchedSlippage = chain.max_slippage_percent;
12226
+ matchedPriceImpact = chain.estimated_price_impact_percent;
12227
+ }
12228
+ return match;
12229
+ })
12230
+ );
12231
+ return {
12232
+ isSupported: found,
12233
+ minimumAmountUsd: matchedMinUsd,
12234
+ estimatedProcessingTime: matchedProcessingTime,
12235
+ maxSlippagePercent: matchedSlippage,
12236
+ priceImpactPercent: matchedPriceImpact,
12237
+ errorMessage: found ? null : `${sourceTokenSymbol || "Source token"} is not a supported withdrawal token. Supported tokens include USDC, USDT, and other stablecoins.`
12238
+ };
12239
+ },
12240
+ enabled: enabled && hasParams,
12241
+ staleTime: 1e3 * 60 * 5,
12242
+ gcTime: 1e3 * 60 * 30,
12243
+ refetchOnMount: false,
12244
+ refetchOnWindowFocus: false
12245
+ });
12246
+ }
12247
+
12248
+ // src/hooks/use-address-balance.ts
12249
+ import { useQuery as useQuery11 } from "@tanstack/react-query";
12250
+ import { getAddressBalance as getAddressBalance2 } from "@unifold/core";
12251
+ function useAddressBalance(params) {
12252
+ const {
12253
+ address,
12254
+ chainType,
12255
+ chainId,
12256
+ tokenAddress,
12257
+ publishableKey,
12258
+ enabled = true
12259
+ } = params;
12260
+ const hasParams = !!address && !!chainType && !!chainId && !!tokenAddress;
12261
+ return useQuery11({
12262
+ queryKey: [
12263
+ "unifold",
12264
+ "addressBalance",
12265
+ address ?? null,
12266
+ chainType ?? null,
12267
+ chainId ?? null,
12268
+ tokenAddress ?? null,
12269
+ publishableKey
12270
+ ],
12271
+ queryFn: async () => {
12272
+ const res = await getAddressBalance2(
12273
+ address,
12274
+ chainType,
12275
+ chainId,
12276
+ tokenAddress,
12277
+ publishableKey
12278
+ );
12279
+ if (res.balance) {
12280
+ const decimals = res.balance.token?.decimals ?? 6;
12281
+ const symbol = res.balance.token?.symbol ?? "";
12282
+ const baseUnit = res.balance.amount;
12283
+ const raw = BigInt(baseUnit);
12284
+ const divisor = BigInt(10 ** decimals);
12285
+ const whole = raw / divisor;
12286
+ const frac = raw % divisor;
12287
+ const fracStr = frac.toString().padStart(decimals, "0").replace(/0+$/, "");
12288
+ const balanceHuman = fracStr ? `${whole}.${fracStr}` : whole.toString();
12289
+ return {
12290
+ balanceBaseUnit: baseUnit,
12291
+ balanceHuman,
12292
+ balanceUsd: res.balance.amount_usd,
12293
+ exchangeRate: res.balance.exchange_rate,
12294
+ decimals,
12295
+ symbol
12296
+ };
12297
+ }
12298
+ return { balanceBaseUnit: "0", balanceHuman: "0", balanceUsd: "0", exchangeRate: null, decimals: 6, symbol: "" };
12299
+ },
12300
+ enabled: enabled && hasParams,
12301
+ staleTime: 1e3 * 30,
12302
+ gcTime: 1e3 * 60 * 5,
12303
+ refetchInterval: 1e3 * 30,
12304
+ refetchOnMount: "always",
12305
+ refetchOnWindowFocus: false
12306
+ });
12307
+ }
12308
+
12309
+ // src/hooks/use-executions.ts
12310
+ import { useQuery as useQuery12 } from "@tanstack/react-query";
12311
+ import { queryExecutions as queryExecutions4, ActionType as ActionType4 } from "@unifold/core";
12312
+ function useExecutions(userId, publishableKey, options) {
12313
+ const actionType = options?.actionType ?? ActionType4.Deposit;
12314
+ return useQuery12({
12315
+ queryKey: ["unifold", "executions", actionType, userId, publishableKey],
12316
+ queryFn: () => queryExecutions4(userId, publishableKey, actionType),
12317
+ enabled: (options?.enabled ?? true) && !!userId,
12318
+ refetchInterval: options?.refetchInterval ?? 3e3,
12319
+ staleTime: 0,
12320
+ gcTime: 1e3 * 60 * 5,
12321
+ refetchOnWindowFocus: false
12322
+ });
12323
+ }
12324
+
12325
+ // src/hooks/use-withdraw-polling.ts
12326
+ import { useState as useState28, useEffect as useEffect22, useRef as useRef7 } from "react";
12327
+ import {
12328
+ queryExecutions as queryExecutions5,
12329
+ pollDirectExecutions as pollDirectExecutions2,
12330
+ ExecutionStatus as ExecutionStatus5,
12331
+ ActionType as ActionType5
12332
+ } from "@unifold/core";
12333
+ var POLL_INTERVAL_MS2 = 2500;
12334
+ var POLL_ENDPOINT_INTERVAL_MS2 = 3e3;
12335
+ var CUTOFF_BUFFER_MS2 = 6e4;
12336
+ function useWithdrawPolling({
12337
+ userId,
12338
+ publishableKey,
12339
+ depositWalletId,
12340
+ enabled = false,
12341
+ onWithdrawSuccess,
12342
+ onWithdrawError
12343
+ }) {
12344
+ const [executions, setExecutions] = useState28([]);
12345
+ const [isPolling, setIsPolling] = useState28(false);
12346
+ const enabledAtRef = useRef7(/* @__PURE__ */ new Date());
12347
+ const trackedRef = useRef7(/* @__PURE__ */ new Map());
12348
+ const prevEnabledRef = useRef7(false);
12349
+ const onSuccessRef = useRef7(onWithdrawSuccess);
12350
+ const onErrorRef = useRef7(onWithdrawError);
12351
+ useEffect22(() => {
12352
+ onSuccessRef.current = onWithdrawSuccess;
12353
+ }, [onWithdrawSuccess]);
12354
+ useEffect22(() => {
12355
+ onErrorRef.current = onWithdrawError;
12356
+ }, [onWithdrawError]);
12357
+ useEffect22(() => {
12358
+ if (enabled && !prevEnabledRef.current) {
12359
+ enabledAtRef.current = /* @__PURE__ */ new Date();
12360
+ trackedRef.current.clear();
12361
+ }
12362
+ if (!enabled) {
12363
+ trackedRef.current.clear();
12364
+ }
12365
+ prevEnabledRef.current = enabled;
12366
+ }, [enabled]);
12367
+ useEffect22(() => {
12368
+ if (!userId || !enabled) return;
12369
+ const enabledAt = enabledAtRef.current;
12370
+ const poll = async () => {
12371
+ try {
12372
+ const response = await queryExecutions5(userId, publishableKey, ActionType5.Withdraw);
12373
+ const cutoff = new Date(enabledAt.getTime() - CUTOFF_BUFFER_MS2);
12374
+ const sorted = [...response.data].sort((a, b) => {
12375
+ const tA = a.created_at ? new Date(a.created_at).getTime() : 0;
12376
+ const tB = b.created_at ? new Date(b.created_at).getTime() : 0;
12377
+ return tB - tA;
12378
+ });
12379
+ const inProgress = [ExecutionStatus5.PENDING, ExecutionStatus5.WAITING, ExecutionStatus5.DELAYED];
12380
+ const terminal = [ExecutionStatus5.SUCCEEDED, ExecutionStatus5.FAILED];
12381
+ let target = null;
12382
+ for (const ex of sorted) {
12383
+ const t11 = ex.created_at ? new Date(ex.created_at) : null;
12384
+ if (!t11 || t11 < cutoff) continue;
12385
+ const prev = trackedRef.current.get(ex.id);
12386
+ if (!prev) {
12387
+ target = ex;
12388
+ break;
12389
+ }
12390
+ if (inProgress.includes(prev) && terminal.includes(ex.status)) {
12391
+ target = ex;
12392
+ break;
12393
+ }
12394
+ }
12395
+ if (target) {
12396
+ const ex = target;
12397
+ const exTime = ex.created_at ? new Date(ex.created_at) : null;
12398
+ if (!exTime || exTime < enabledAtRef.current) return;
12399
+ const prev = trackedRef.current.get(ex.id);
12400
+ trackedRef.current.set(ex.id, ex.status);
12401
+ setExecutions((list) => {
12402
+ const idx = list.findIndex((e) => e.id === ex.id);
12403
+ if (idx >= 0) {
12404
+ const u = [...list];
12405
+ u[idx] = ex;
12406
+ return u;
12407
+ }
12408
+ return [...list, ex];
12409
+ });
12410
+ if (ex.status === ExecutionStatus5.SUCCEEDED && (!prev || inProgress.includes(prev))) {
12411
+ onSuccessRef.current?.({ message: "Withdrawal completed successfully", executionId: ex.id, transaction: ex });
12412
+ } else if (ex.status === ExecutionStatus5.FAILED && prev !== ExecutionStatus5.FAILED) {
12413
+ onErrorRef.current?.({ message: "Withdrawal failed", code: "WITHDRAW_FAILED", error: ex });
12414
+ }
12415
+ }
12416
+ } catch (error) {
12417
+ console.error("Failed to fetch withdraw executions:", error);
12418
+ onErrorRef.current?.({ message: "Failed to fetch withdrawal status", code: "POLLING_ERROR", error });
12419
+ }
12420
+ };
12421
+ void poll();
12422
+ const interval = setInterval(poll, POLL_INTERVAL_MS2);
12423
+ setIsPolling(true);
12424
+ return () => {
12425
+ clearInterval(interval);
12426
+ setIsPolling(false);
12427
+ };
12428
+ }, [userId, publishableKey, enabled]);
12429
+ useEffect22(() => {
12430
+ if (!enabled || !depositWalletId) return;
12431
+ const trigger = async () => {
12432
+ try {
12433
+ await pollDirectExecutions2({ deposit_wallet_id: depositWalletId }, publishableKey);
12434
+ } catch {
12435
+ }
12436
+ };
12437
+ trigger();
12438
+ const interval = setInterval(trigger, POLL_ENDPOINT_INTERVAL_MS2);
12439
+ return () => clearInterval(interval);
12440
+ }, [enabled, depositWalletId, publishableKey]);
12441
+ return { executions, isPolling };
12442
+ }
12443
+
12444
+ // src/components/withdrawals/WithdrawDoubleInput.tsx
12445
+ import { jsx as jsx52, jsxs as jsxs45 } from "react/jsx-runtime";
12446
+ var t7 = i18n.withdrawModal;
12447
+ var getChainKey4 = (chainId, chainType) => `${chainType}:${chainId}`;
12448
+ function WithdrawDoubleInput({
12449
+ tokens,
12450
+ selectedTokenSymbol,
12451
+ selectedChainKey,
12452
+ onTokenChange,
12453
+ onChainChange,
12454
+ isLoading = false
12455
+ }) {
12456
+ const { fonts, components } = useTheme();
12457
+ const isDarkMode = useTheme().themeClass.includes("uf-dark");
12458
+ const selectedToken = selectedTokenSymbol ? tokens.find((t11) => t11.symbol === selectedTokenSymbol) : void 0;
12459
+ const availableChainsForToken = selectedToken?.chains || [];
12460
+ const renderTokenItem = (tokenData) => /* @__PURE__ */ jsxs45("div", { className: "uf-flex uf-items-center uf-gap-2", children: [
12461
+ /* @__PURE__ */ jsx52(
12462
+ "img",
12463
+ {
12464
+ src: tokenData.icon_url,
12465
+ alt: tokenData.symbol,
12466
+ width: 20,
12467
+ height: 20,
12468
+ loading: "lazy",
12469
+ className: "uf-rounded-full uf-flex-shrink-0"
12470
+ }
12471
+ ),
12472
+ /* @__PURE__ */ jsx52("span", { className: "uf-text-xs uf-font-normal", children: tokenData.symbol })
12473
+ ] });
12474
+ const renderChainItem = (chainData) => /* @__PURE__ */ jsxs45("div", { className: "uf-flex uf-items-center uf-gap-2", children: [
12475
+ /* @__PURE__ */ jsx52(
12476
+ "img",
12477
+ {
12478
+ src: chainData.icon_url,
12479
+ alt: chainData.chain_name,
12480
+ width: 20,
12481
+ height: 20,
12482
+ loading: "lazy",
12483
+ className: "uf-rounded-full uf-flex-shrink-0"
12484
+ }
12485
+ ),
12486
+ /* @__PURE__ */ jsx52("span", { className: "uf-text-xs uf-font-normal", children: chainData.chain_name })
12487
+ ] });
12488
+ const currentChainData = selectedChainKey ? availableChainsForToken.find(
12489
+ (c) => getChainKey4(c.chain_id, c.chain_type) === selectedChainKey
12490
+ ) : void 0;
12491
+ return /* @__PURE__ */ jsxs45("div", { className: "uf-grid uf-grid-cols-2 uf-gap-2.5", children: [
12492
+ /* @__PURE__ */ jsxs45("div", { children: [
12493
+ /* @__PURE__ */ jsx52(
12494
+ "div",
12495
+ {
12496
+ className: "uf-text-xs uf-mb-2 uf-flex uf-items-center uf-gap-1",
12497
+ style: { color: components.card.labelColor },
12498
+ children: t7.receiveToken
12499
+ }
12500
+ ),
12501
+ /* @__PURE__ */ jsxs45(
12502
+ Select,
12503
+ {
12504
+ value: selectedTokenSymbol ?? "",
12505
+ onValueChange: onTokenChange,
12506
+ disabled: isLoading || tokens.length === 0,
12507
+ children: [
12508
+ /* @__PURE__ */ jsx52(
12509
+ SelectTrigger,
12510
+ {
12511
+ className: "uf-h-10 hover:uf-opacity-90 uf-text-foreground disabled:uf-opacity-50",
12512
+ style: {
12513
+ backgroundColor: components.card.backgroundColor,
12514
+ border: `${components.card.borderWidth}px solid ${components.card.borderColor}`
12515
+ },
12516
+ children: /* @__PURE__ */ jsx52(SelectValue, { children: isLoading || !selectedTokenSymbol ? /* @__PURE__ */ jsx52("span", { className: "uf-text-xs uf-font-light uf-text-muted-foreground", children: t7.loading }) : selectedToken ? renderTokenItem(selectedToken) : /* @__PURE__ */ jsx52("span", { className: "uf-text-xs uf-font-normal", children: selectedTokenSymbol }) })
12517
+ }
12518
+ ),
12519
+ /* @__PURE__ */ jsx52(
12520
+ SelectContent,
12521
+ {
12522
+ className: "uf-bg-secondary uf-border uf-text-foreground uf-max-h-[300px]",
12523
+ style: {
12524
+ border: `1px solid ${isDarkMode ? "rgba(255,255,255,0.15)" : "rgba(0,0,0,0.15)"}`,
12525
+ ...fonts.regular ? { "--uf-font-family": fonts.regular } : {}
12526
+ },
12527
+ children: tokens.map((tokenData) => /* @__PURE__ */ jsx52(
12528
+ SelectItem,
12529
+ {
12530
+ value: tokenData.symbol,
12531
+ className: "focus:uf-bg-accent focus:uf-text-foreground",
12532
+ children: renderTokenItem(tokenData)
12533
+ },
12534
+ tokenData.symbol
12535
+ ))
12536
+ }
12537
+ )
12538
+ ]
12539
+ }
12540
+ )
12541
+ ] }),
12542
+ /* @__PURE__ */ jsxs45("div", { children: [
12543
+ /* @__PURE__ */ jsx52(
12544
+ "div",
12545
+ {
12546
+ className: "uf-text-xs uf-mb-2 uf-flex uf-items-center uf-gap-1",
12547
+ style: { color: components.card.labelColor },
12548
+ children: t7.receiveChain
12549
+ }
12550
+ ),
12551
+ /* @__PURE__ */ jsxs45(
12552
+ Select,
12553
+ {
12554
+ value: selectedChainKey ?? "",
12555
+ onValueChange: onChainChange,
12556
+ disabled: isLoading || availableChainsForToken.length === 0,
12557
+ children: [
12558
+ /* @__PURE__ */ jsx52(
12559
+ SelectTrigger,
12560
+ {
12561
+ className: "uf-h-10 hover:uf-opacity-90 uf-text-foreground disabled:uf-opacity-50",
12562
+ style: {
12563
+ backgroundColor: components.card.backgroundColor,
12564
+ border: `${components.card.borderWidth}px solid ${components.card.borderColor}`
12565
+ },
12566
+ children: /* @__PURE__ */ jsx52(SelectValue, { children: isLoading || !selectedChainKey ? /* @__PURE__ */ jsx52("span", { className: "uf-text-xs uf-font-light uf-text-muted-foreground", children: t7.loading }) : currentChainData ? renderChainItem(currentChainData) : /* @__PURE__ */ jsx52("span", { className: "uf-text-xs uf-font-normal", children: selectedChainKey }) })
12567
+ }
12568
+ ),
12569
+ /* @__PURE__ */ jsx52(
12570
+ SelectContent,
12571
+ {
12572
+ align: "end",
12573
+ className: "uf-bg-secondary uf-border uf-text-foreground uf-max-h-[300px] uf-min-w-[200px]",
12574
+ style: {
12575
+ border: `1px solid ${isDarkMode ? "rgba(255,255,255,0.15)" : "rgba(0,0,0,0.15)"}`,
12576
+ ...fonts.regular ? { "--uf-font-family": fonts.regular } : {}
12577
+ },
12578
+ children: availableChainsForToken.length === 0 ? /* @__PURE__ */ jsx52("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) => {
12579
+ const chainKey = getChainKey4(chainData.chain_id, chainData.chain_type);
12580
+ return /* @__PURE__ */ jsx52(
12581
+ SelectItem,
12582
+ {
12583
+ value: chainKey,
12584
+ className: "focus:uf-bg-accent focus:uf-text-foreground",
12585
+ children: renderChainItem(chainData)
12586
+ },
12587
+ chainKey
12588
+ );
12589
+ })
12590
+ }
12591
+ )
12592
+ ]
12593
+ }
12594
+ )
12595
+ ] })
12596
+ ] });
12597
+ }
12598
+
12599
+ // src/components/withdrawals/WithdrawForm.tsx
12600
+ import { useState as useState29, useCallback as useCallback5, useMemo as useMemo10, useEffect as useEffect23 } from "react";
12601
+ import {
12602
+ AlertTriangle as AlertTriangle2,
12603
+ ArrowUpDown,
12604
+ ChevronDown as ChevronDown7,
12605
+ ChevronUp as ChevronUp6,
12606
+ Clock as Clock4,
12607
+ ClipboardPaste,
12608
+ DollarSign as DollarSign3,
12609
+ Loader2 as Loader25,
12610
+ ShieldCheck as ShieldCheck3,
12611
+ Wallet as Wallet3
12612
+ } from "lucide-react";
12613
+
12614
+ // src/hooks/use-verify-recipient-address.ts
12615
+ import { useQuery as useQuery13 } from "@tanstack/react-query";
12616
+ import { verifyRecipientAddress as verifyRecipientAddress2 } from "@unifold/core";
12617
+ function useVerifyRecipientAddress(params) {
12618
+ const {
12619
+ chainType,
12620
+ chainId,
12621
+ tokenAddress,
12622
+ recipientAddress,
12623
+ publishableKey,
12624
+ enabled = true
12625
+ } = params;
12626
+ const trimmedAddress = recipientAddress?.trim() || "";
12627
+ const hasAllParams = !!chainType && !!chainId && !!tokenAddress && trimmedAddress.length > 0;
12628
+ return useQuery13({
12629
+ queryKey: [
12630
+ "unifold",
12631
+ "verifyRecipientAddress",
12632
+ chainType ?? null,
12633
+ chainId ?? null,
12634
+ tokenAddress ?? null,
12635
+ trimmedAddress,
12636
+ publishableKey
12637
+ ],
12638
+ queryFn: () => verifyRecipientAddress2(
12639
+ {
12640
+ chain_type: chainType,
12641
+ chain_id: chainId,
12642
+ token_address: tokenAddress,
12643
+ recipient_address: trimmedAddress
12644
+ },
12645
+ publishableKey
12646
+ ),
12647
+ enabled: enabled && hasAllParams,
12648
+ staleTime: 1e3 * 60 * 5,
12649
+ gcTime: 1e3 * 60 * 30,
12650
+ retry: 1,
12651
+ refetchOnMount: false,
12652
+ refetchOnWindowFocus: false
12653
+ });
12654
+ }
12655
+
12656
+ // src/components/withdrawals/send-withdraw.ts
12657
+ import {
12658
+ buildSolanaTransaction as buildSolanaTransaction2,
12659
+ sendSolanaTransaction as sendSolanaTransactionToBackend2
12660
+ } from "@unifold/core";
12661
+ async function sendEvmWithdraw(params) {
12662
+ const {
12663
+ provider,
12664
+ fromAddress,
12665
+ depositWalletAddress,
12666
+ sourceTokenAddress,
12667
+ sourceChainId,
12668
+ amountBaseUnit
12669
+ } = params;
12670
+ const currentChainIdHex = await provider.request({
12671
+ method: "eth_chainId",
12672
+ params: []
12673
+ });
12674
+ const currentChainId = parseInt(currentChainIdHex, 16).toString();
12675
+ if (currentChainId !== sourceChainId) {
12676
+ const requiredHex = "0x" + parseInt(sourceChainId).toString(16);
12677
+ try {
12678
+ await provider.request({
12679
+ method: "wallet_switchEthereumChain",
12680
+ params: [{ chainId: requiredHex }]
12681
+ });
12682
+ const newHex = await provider.request({ method: "eth_chainId", params: [] });
12683
+ if (parseInt(newHex, 16).toString() !== sourceChainId) {
12684
+ throw new Error(`Failed to switch to chain ${sourceChainId}. Please switch manually.`);
12685
+ }
12686
+ } catch (err) {
12687
+ if (err && typeof err === "object" && "code" in err) {
12688
+ const e = err;
12689
+ if (e.code === 4902) throw new Error(`Chain ${sourceChainId} is not configured in your wallet.`);
12690
+ if (e.code === 4001) throw new Error("You must approve the network switch to withdraw.");
12691
+ }
12692
+ throw err;
12693
+ }
12694
+ }
12695
+ const isNative = sourceTokenAddress === "native" || sourceTokenAddress === "0x0000000000000000000000000000000000000000" || sourceTokenAddress === "";
12696
+ const amountBig = BigInt(amountBaseUnit);
12697
+ const txParams = isNative ? { from: fromAddress, to: depositWalletAddress, value: "0x" + amountBig.toString(16) } : {
12698
+ from: fromAddress,
12699
+ to: sourceTokenAddress,
12700
+ data: "0xa9059cbb" + depositWalletAddress.slice(2).padStart(64, "0") + amountBig.toString(16).padStart(64, "0")
12701
+ };
12702
+ let gasEstimate;
12703
+ try {
12704
+ const hex = await provider.request({ method: "eth_estimateGas", params: [txParams] });
12705
+ gasEstimate = BigInt(hex);
12706
+ } catch {
12707
+ gasEstimate = isNative ? BigInt(21e3) : BigInt(65e3);
12708
+ }
12709
+ const gasPrice = BigInt(await provider.request({ method: "eth_gasPrice", params: [] }));
12710
+ const gasWithBuffer = gasEstimate * BigInt(120) / BigInt(100);
12711
+ const gasCost = gasWithBuffer * gasPrice;
12712
+ const ethBalance = BigInt(
12713
+ await provider.request({ method: "eth_getBalance", params: [fromAddress, "latest"] })
12714
+ );
12715
+ const totalRequired = isNative ? gasCost + amountBig : gasCost;
12716
+ if (ethBalance < totalRequired) {
12717
+ const gasFmt = (Number(gasCost) / 1e18).toFixed(6);
12718
+ if (isNative) {
12719
+ throw new Error(`Insufficient balance. Need ${(Number(totalRequired) / 1e18).toFixed(6)} ETH (amount + ~${gasFmt} gas).`);
12720
+ }
12721
+ throw new Error(`Insufficient ETH for gas. Need ~${gasFmt} ETH for fees.`);
12722
+ }
12723
+ const txHash = await provider.request({ method: "eth_sendTransaction", params: [txParams] });
12724
+ return txHash;
12725
+ }
12726
+ async function sendSolanaWithdraw(params) {
12727
+ const {
12728
+ provider,
12729
+ fromAddress,
12730
+ depositWalletAddress,
12731
+ sourceTokenAddress,
12732
+ amountBaseUnit,
12733
+ publishableKey
12734
+ } = params;
12735
+ if (!provider.publicKey) {
12736
+ await provider.connect();
12737
+ }
12738
+ const buildResponse = await buildSolanaTransaction2(
12739
+ {
12740
+ chain_id: "mainnet",
12741
+ token_address: sourceTokenAddress === "" ? "native" : sourceTokenAddress,
12742
+ source_address: fromAddress,
12743
+ destination_address: depositWalletAddress,
12744
+ amount: amountBaseUnit
12745
+ },
12746
+ publishableKey
12747
+ );
12748
+ const { VersionedTransaction } = await import(
12749
+ /* @vite-ignore */
12750
+ "@solana/web3.js"
12751
+ );
12752
+ const binaryString = atob(buildResponse.transaction);
12753
+ const bytes = new Uint8Array(binaryString.length);
12754
+ for (let i = 0; i < binaryString.length; i++) {
12755
+ bytes[i] = binaryString.charCodeAt(i);
12756
+ }
12757
+ const transaction = VersionedTransaction.deserialize(bytes);
12758
+ const signedTransaction = await provider.signTransaction(transaction);
12759
+ const serialized = signedTransaction.serialize();
12760
+ let binaryStr = "";
12761
+ for (let i = 0; i < serialized.length; i++) {
12762
+ binaryStr += String.fromCharCode(serialized[i]);
12763
+ }
12764
+ const sendResponse = await sendSolanaTransactionToBackend2(
12765
+ { chain_id: "mainnet", signed_transaction: btoa(binaryStr) },
12766
+ publishableKey
12767
+ );
12768
+ return sendResponse.signature;
12769
+ }
12770
+ async function detectBrowserWallet(chainType, senderAddress) {
12771
+ const win = typeof window !== "undefined" ? window : null;
12772
+ if (!win || !senderAddress) return null;
12773
+ const anyWin = win;
12774
+ if (chainType === "solana") {
12775
+ const solProviders = [];
12776
+ if (win.phantom?.solana) solProviders.push({ provider: win.phantom.solana, name: "Phantom" });
12777
+ if (anyWin.solflare) solProviders.push({ provider: anyWin.solflare, name: "Solflare" });
12778
+ if (anyWin.backpack) solProviders.push({ provider: anyWin.backpack, name: "Backpack" });
12779
+ if (anyWin.trustwallet?.solana) solProviders.push({ provider: anyWin.trustwallet.solana, name: "Trust Wallet" });
12780
+ for (const { provider, name } of solProviders) {
12781
+ if (!provider) continue;
12782
+ try {
12783
+ let addr;
12784
+ if (provider.isConnected && provider.publicKey) {
12785
+ addr = provider.publicKey.toString();
12786
+ } else {
12787
+ const resp = await provider.connect({ onlyIfTrusted: true });
12788
+ if (resp?.publicKey) addr = resp.publicKey.toString();
12789
+ }
12790
+ if (addr && addr === senderAddress) {
12791
+ return { chainFamily: "solana", provider, name, address: addr };
12792
+ }
12793
+ } catch {
12794
+ }
12795
+ }
12796
+ }
12797
+ if (chainType === "ethereum") {
12798
+ const evmProviders = [];
12799
+ const seen = /* @__PURE__ */ new Set();
12800
+ const add = (p, name) => {
12801
+ if (p && typeof p.request === "function" && !seen.has(p)) {
12802
+ seen.add(p);
12803
+ evmProviders.push({ provider: p, name });
12804
+ }
12805
+ };
12806
+ add(anyWin.phantom?.ethereum, "Phantom");
12807
+ add(anyWin.coinbaseWalletExtension, "Coinbase");
12808
+ add(anyWin.trustwallet?.ethereum, "Trust Wallet");
12809
+ add(anyWin.okxwallet, "OKX Wallet");
12810
+ if (anyWin.__eip6963Providers) {
12811
+ for (const detail of anyWin.__eip6963Providers) {
12812
+ const rdns = detail.info?.rdns || "";
12813
+ let name = detail.info?.name || "Wallet";
12814
+ if (rdns.includes("metamask")) name = "MetaMask";
12815
+ else if (rdns.includes("rabby")) name = "Rabby";
12816
+ else if (rdns.includes("rainbow")) name = "Rainbow";
12817
+ add(detail.provider, name);
12818
+ }
12819
+ }
12820
+ if (win.ethereum) {
12821
+ const eth = win.ethereum;
12822
+ let name = "Wallet";
12823
+ if (eth.isMetaMask && !eth.isPhantom && !eth.isRabby) name = "MetaMask";
12824
+ else if (eth.isRabby) name = "Rabby";
12825
+ else if (eth.isRainbow) name = "Rainbow";
12826
+ else if (eth.isCoinbaseWallet) name = "Coinbase";
12827
+ add(eth, name);
12828
+ }
12829
+ for (const { provider, name } of evmProviders) {
12830
+ try {
12831
+ const accounts = await provider.request({ method: "eth_accounts" });
12832
+ if (accounts?.length > 0 && accounts[0].toLowerCase() === senderAddress.toLowerCase()) {
12833
+ return { chainFamily: "evm", provider, name, address: accounts[0] };
12834
+ }
12835
+ } catch {
12836
+ }
12837
+ }
12838
+ }
12839
+ return null;
12840
+ }
12841
+
12842
+ // src/components/withdrawals/WithdrawForm.tsx
12843
+ import { Fragment as Fragment10, jsx as jsx53, jsxs as jsxs46 } from "react/jsx-runtime";
12844
+ var t8 = i18n.withdrawModal;
12845
+ var tCrypto = i18n.transferCrypto;
12846
+ function formatProcessingTime2(seconds) {
12847
+ if (seconds === null) {
12848
+ return tCrypto.processingTime.lessThanMinutes.replace("{{minutes}}", "1");
12849
+ }
12850
+ const minutes = Math.ceil(seconds / 60);
12851
+ if (minutes < 60) {
12852
+ return tCrypto.processingTime.lessThanMinutes.replace("{{minutes}}", String(minutes));
12853
+ }
12854
+ const hours = Math.ceil(minutes / 60);
12855
+ return tCrypto.processingTime.lessThanHours.replace("{{hours}}", String(hours));
12856
+ }
12857
+ function computeBaseUnit(balanceBaseUnit, inputAmount, balanceAmount) {
12858
+ if (balanceAmount <= 0 || inputAmount <= 0) return "0";
12859
+ if (inputAmount >= balanceAmount) return balanceBaseUnit;
12860
+ const PRECISION = 10n ** 18n;
12861
+ const ratioScaled = BigInt(Math.round(inputAmount / balanceAmount * Number(PRECISION)));
12862
+ const result = BigInt(balanceBaseUnit) * ratioScaled / PRECISION;
12863
+ return result.toString();
12864
+ }
12865
+ function toSafeDecimalString(n, maxDecimals) {
12866
+ if (n === 0) return "0";
12867
+ return n.toFixed(maxDecimals).replace(/\.?0+$/, "");
12868
+ }
12869
+ function WithdrawForm({
12870
+ publishableKey,
12871
+ externalUserId,
12872
+ sourceChainType,
12873
+ selectedToken,
12874
+ selectedChain,
12875
+ sourceTokenSymbol,
12876
+ recipientAddressProp,
12877
+ balanceData,
12878
+ isLoadingBalance,
12879
+ minimumWithdrawAmountUsd,
12880
+ estimatedProcessingTime,
12881
+ maxSlippagePercent,
12882
+ priceImpactPercent,
12883
+ detectedWallet,
12884
+ sourceChainId,
12885
+ sourceTokenAddress,
12886
+ isWalletMatch,
12887
+ connectedWalletName,
12888
+ canWithdraw,
12889
+ onWithdraw,
12890
+ onWithdrawError,
12891
+ onDepositWalletCreation,
12892
+ onWithdrawSubmitted,
12893
+ footerLeft
12894
+ }) {
12895
+ const { colors: colors2, fonts, components } = useTheme();
12896
+ const [recipientAddress, setRecipientAddress] = useState29(recipientAddressProp || "");
12897
+ const [amount, setAmount] = useState29("");
12898
+ const [inputUnit, setInputUnit] = useState29("crypto");
12899
+ const [isSubmitting, setIsSubmitting] = useState29(false);
12900
+ const [submitError, setSubmitError] = useState29(null);
12901
+ const [detailsExpanded, setDetailsExpanded] = useState29(false);
12902
+ const [glossaryOpen, setGlossaryOpen] = useState29(false);
12903
+ useEffect23(() => {
12904
+ setRecipientAddress(recipientAddressProp || "");
12905
+ setAmount("");
12906
+ setInputUnit("crypto");
12907
+ setSubmitError(null);
12908
+ }, [recipientAddressProp]);
12909
+ const trimmedAddress = recipientAddress.trim();
12910
+ const [debouncedAddress, setDebouncedAddress] = useState29(trimmedAddress);
12911
+ useEffect23(() => {
12912
+ const id = setTimeout(() => setDebouncedAddress(trimmedAddress), 500);
12913
+ return () => clearTimeout(id);
12914
+ }, [trimmedAddress]);
12915
+ const {
12916
+ data: addressVerification,
12917
+ isLoading: isVerifyingAddress,
12918
+ error: verifyError
12919
+ } = useVerifyRecipientAddress({
12920
+ chainType: selectedChain?.chain_type,
12921
+ chainId: selectedChain?.chain_id,
12922
+ tokenAddress: selectedChain?.token_address,
12923
+ recipientAddress: debouncedAddress,
12924
+ publishableKey,
12925
+ enabled: debouncedAddress.length > 5 && !!selectedChain
12926
+ });
12927
+ const isDebouncing = trimmedAddress !== debouncedAddress;
12928
+ const addressError = useMemo10(() => {
12929
+ if (!trimmedAddress || trimmedAddress.length <= 5) return null;
12930
+ if (isDebouncing || isVerifyingAddress) return null;
12931
+ if (verifyError) return t8.invalidAddress;
12932
+ if (addressVerification && !addressVerification.valid) {
12933
+ if (addressVerification.failure_code === "account_not_found")
12934
+ return `Account not found on ${selectedChain?.chain_name}`;
12935
+ if (addressVerification.failure_code === "not_opted_in")
12936
+ return `Recipient has not opted in to ${selectedToken?.symbol} on ${selectedChain?.chain_name}`;
12937
+ return t8.invalidAddress;
12938
+ }
12939
+ return null;
12940
+ }, [trimmedAddress, isDebouncing, isVerifyingAddress, verifyError, addressVerification, selectedChain, selectedToken]);
12941
+ const isAddressValid = !isDebouncing && !!addressVerification?.valid && !addressError;
12942
+ const exchangeRate = useMemo10(() => {
12943
+ if (!balanceData?.exchangeRate) return 0;
12944
+ return parseFloat(balanceData.exchangeRate);
12945
+ }, [balanceData]);
12946
+ const balanceCrypto = useMemo10(() => {
12947
+ if (!balanceData?.balanceHuman) return 0;
12948
+ return parseFloat(balanceData.balanceHuman);
12949
+ }, [balanceData]);
12950
+ const balanceUsdNum = useMemo10(() => {
12951
+ if (!balanceData?.balanceUsd) return 0;
12952
+ return parseFloat(balanceData.balanceUsd);
12953
+ }, [balanceData]);
12954
+ const tokenSymbol = sourceTokenSymbol || balanceData?.symbol || "TOKEN";
12955
+ const sourceDecimals = balanceData?.decimals ?? 6;
12956
+ const cryptoAmountFromInput = useMemo10(() => {
12957
+ const val = parseFloat(amount);
12958
+ if (!val || val <= 0) return 0;
12959
+ if (inputUnit === "crypto") return val;
12960
+ return exchangeRate > 0 ? val / exchangeRate : 0;
12961
+ }, [amount, inputUnit, exchangeRate]);
12962
+ const fiatAmountFromInput = useMemo10(() => {
12963
+ const val = parseFloat(amount);
12964
+ if (!val || val <= 0) return 0;
12965
+ if (inputUnit === "fiat") return val;
12966
+ return val * exchangeRate;
12967
+ }, [amount, inputUnit, exchangeRate]);
12968
+ const convertedDisplay = useMemo10(() => {
12969
+ if (!amount || parseFloat(amount) <= 0) return null;
12970
+ if (inputUnit === "crypto") {
12971
+ return `$${fiatAmountFromInput.toLocaleString(void 0, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`;
12972
+ }
12973
+ return `${cryptoAmountFromInput.toLocaleString(void 0, { minimumFractionDigits: 2, maximumFractionDigits: 6 })} ${tokenSymbol}`;
12974
+ }, [amount, inputUnit, fiatAmountFromInput, cryptoAmountFromInput, tokenSymbol]);
12975
+ const balanceDisplay = useMemo10(() => {
12976
+ if (isLoadingBalance || !balanceData) return null;
12977
+ if (inputUnit === "crypto") {
12978
+ return `${balanceCrypto.toLocaleString(void 0, { minimumFractionDigits: 2, maximumFractionDigits: 2 })} ${tokenSymbol}`;
12979
+ }
12980
+ return `$${balanceUsdNum.toLocaleString(void 0, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`;
12981
+ }, [isLoadingBalance, balanceData, inputUnit, balanceCrypto, balanceUsdNum, tokenSymbol]);
12982
+ const handleSwitchUnit = useCallback5(() => {
12983
+ const val = parseFloat(amount);
12984
+ if (!val || val <= 0 || exchangeRate <= 0) {
12985
+ setInputUnit((u) => u === "crypto" ? "fiat" : "crypto");
12986
+ setAmount("");
12987
+ return;
12988
+ }
12989
+ if (inputUnit === "crypto") {
12990
+ const fiat = val * exchangeRate;
12991
+ setAmount(fiat.toFixed(2));
12992
+ setInputUnit("fiat");
12993
+ } else {
12994
+ const crypto = val / exchangeRate;
12995
+ setAmount(crypto.toFixed(sourceDecimals > 6 ? 6 : sourceDecimals));
12996
+ setInputUnit("crypto");
12997
+ }
12998
+ }, [amount, inputUnit, exchangeRate, sourceDecimals]);
12999
+ const handleMaxClick = useCallback5(() => {
13000
+ if (inputUnit === "crypto") {
13001
+ if (balanceCrypto <= 0) return;
13002
+ setAmount(balanceData?.balanceHuman ?? "0");
13003
+ } else {
13004
+ if (balanceUsdNum <= 0) return;
13005
+ setAmount((Math.floor(balanceUsdNum * 100) / 100).toFixed(2));
13006
+ }
13007
+ }, [inputUnit, balanceCrypto, balanceUsdNum, balanceData]);
13008
+ const isBelowMinimum = minimumWithdrawAmountUsd !== null && fiatAmountFromInput > 0 && fiatAmountFromInput < minimumWithdrawAmountUsd;
13009
+ const isOverBalance = inputUnit === "crypto" ? cryptoAmountFromInput > 0 && balanceCrypto > 0 && cryptoAmountFromInput > balanceCrypto : fiatAmountFromInput > 0 && balanceUsdNum > 0 && fiatAmountFromInput > balanceUsdNum;
13010
+ const isFormValid = trimmedAddress.length > 0 && amount.trim().length > 0 && cryptoAmountFromInput > 0 && isAddressValid && !isBelowMinimum && !isOverBalance && !!balanceData;
13011
+ const handleWithdraw = useCallback5(async () => {
13012
+ if (!selectedToken || !selectedChain) return;
13013
+ if (!isFormValid) return;
13014
+ setIsSubmitting(true);
13015
+ setSubmitError(null);
13016
+ try {
13017
+ const depositWallet = await onDepositWalletCreation({
13018
+ destinationChainType: selectedChain.chain_type,
13019
+ destinationChainId: selectedChain.chain_id,
13020
+ destinationTokenAddress: selectedChain.token_address,
13021
+ recipientAddress: trimmedAddress
13022
+ });
13023
+ const amountBaseUnit = computeBaseUnit(
13024
+ balanceData.balanceBaseUnit,
13025
+ parseFloat(amount),
13026
+ inputUnit === "crypto" ? balanceCrypto : balanceUsdNum
13027
+ );
13028
+ const humanAmount = toSafeDecimalString(cryptoAmountFromInput, sourceDecimals);
13029
+ const txInfo = {
13030
+ sourceChainType,
13031
+ sourceChainId,
13032
+ sourceTokenAddress,
13033
+ sourceTokenSymbol: tokenSymbol,
13034
+ destinationChainType: selectedChain.chain_type,
13035
+ destinationChainId: selectedChain.chain_id,
13036
+ destinationTokenAddress: selectedChain.token_address,
13037
+ destinationTokenSymbol: selectedToken.symbol,
13038
+ amount: humanAmount,
13039
+ amountBaseUnit,
13040
+ withdrawIntentAddress: depositWallet.address,
13041
+ recipientAddress: trimmedAddress
13042
+ };
13043
+ if (detectedWallet) {
13044
+ if (detectedWallet.chainFamily === "evm") {
13045
+ await sendEvmWithdraw({
13046
+ provider: detectedWallet.provider,
13047
+ fromAddress: detectedWallet.address,
13048
+ depositWalletAddress: depositWallet.address,
13049
+ sourceTokenAddress,
13050
+ sourceChainId,
13051
+ amountBaseUnit
13052
+ });
13053
+ } else if (detectedWallet.chainFamily === "solana") {
13054
+ await sendSolanaWithdraw({
13055
+ provider: detectedWallet.provider,
13056
+ fromAddress: detectedWallet.address,
13057
+ depositWalletAddress: depositWallet.address,
13058
+ sourceTokenAddress,
13059
+ amountBaseUnit,
13060
+ publishableKey
13061
+ });
13062
+ }
13063
+ } else if (onWithdraw) {
13064
+ await onWithdraw(txInfo);
13065
+ } else {
13066
+ throw new Error("No withdrawal method available. Please connect a wallet.");
13067
+ }
13068
+ onWithdrawSubmitted?.(txInfo);
13069
+ } catch (err) {
13070
+ const raw = err instanceof Error ? err.message : "Withdrawal failed. Please try again.";
13071
+ setSubmitError(raw.length > 120 ? "Withdrawal failed. Please try again." : raw);
13072
+ onWithdrawError?.({
13073
+ message: raw,
13074
+ error: err,
13075
+ code: "WITHDRAW_FAILED"
13076
+ });
13077
+ } finally {
13078
+ setIsSubmitting(false);
13079
+ }
13080
+ }, [selectedToken, selectedChain, isFormValid, cryptoAmountFromInput, sourceDecimals, trimmedAddress, publishableKey, onWithdraw, detectedWallet, sourceTokenAddress, sourceChainId, onWithdrawError, onDepositWalletCreation, onWithdrawSubmitted, amount, inputUnit, balanceCrypto, balanceUsdNum, balanceData]);
13081
+ return /* @__PURE__ */ jsxs46(Fragment10, { children: [
13082
+ /* @__PURE__ */ jsxs46("div", { children: [
13083
+ /* @__PURE__ */ jsx53(
13084
+ "div",
13085
+ {
13086
+ className: "uf-text-xs uf-mb-1.5",
13087
+ style: { color: components.card.labelColor, fontFamily: fonts.medium },
13088
+ children: t8.recipientAddress
13089
+ }
13090
+ ),
13091
+ /* @__PURE__ */ jsx53(
13092
+ "style",
13093
+ {
13094
+ dangerouslySetInnerHTML: {
13095
+ __html: `.uf-withdraw-addr::placeholder { color: ${components.search.placeholderColor}; }`
13096
+ }
13097
+ }
13098
+ ),
13099
+ /* @__PURE__ */ jsxs46(
13100
+ "div",
13101
+ {
13102
+ className: "uf-flex uf-items-center uf-gap-1 uf-pr-2",
13103
+ style: {
13104
+ backgroundColor: components.search.backgroundColor,
13105
+ borderRadius: components.input.borderRadius,
13106
+ border: `${components.input.borderWidth}px solid ${addressError ? colors2.error : components.input.borderColor}`
13107
+ },
13108
+ children: [
13109
+ /* @__PURE__ */ jsx53(
13110
+ "input",
13111
+ {
13112
+ type: "text",
13113
+ placeholder: t8.recipientAddressPlaceholder,
13114
+ value: recipientAddress,
13115
+ onChange: (e) => {
13116
+ setRecipientAddress(e.target.value);
13117
+ setSubmitError(null);
13118
+ },
13119
+ 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",
13120
+ style: {
13121
+ color: components.search.inputColor,
13122
+ fontFamily: fonts.regular
13123
+ }
13124
+ }
13125
+ ),
13126
+ /* @__PURE__ */ jsx53(
13127
+ "button",
13128
+ {
13129
+ type: "button",
13130
+ onClick: async () => {
13131
+ try {
13132
+ const text = await navigator.clipboard.readText();
13133
+ if (text) {
13134
+ setRecipientAddress(text.trim());
13135
+ setSubmitError(null);
13136
+ }
13137
+ } catch {
13138
+ }
13139
+ },
13140
+ className: "uf-flex-shrink-0 uf-p-1 uf-rounded uf-transition-colors hover:uf-opacity-70",
13141
+ style: { color: colors2.foregroundMuted },
13142
+ title: "Paste from clipboard",
13143
+ children: /* @__PURE__ */ jsx53(ClipboardPaste, { className: "uf-w-4 uf-h-4" })
13144
+ }
13145
+ )
13146
+ ]
13147
+ }
13148
+ ),
13149
+ (isDebouncing || isVerifyingAddress) && trimmedAddress.length > 5 && /* @__PURE__ */ jsxs46("div", { className: "uf-flex uf-items-center uf-gap-1.5 uf-mt-1.5", children: [
13150
+ /* @__PURE__ */ jsx53(Loader25, { className: "uf-w-3 uf-h-3 uf-animate-spin", style: { color: colors2.foregroundMuted } }),
13151
+ /* @__PURE__ */ jsx53("span", { className: "uf-text-xs", style: { color: colors2.foregroundMuted, fontFamily: fonts.regular }, children: t8.verifyingAddress })
13152
+ ] }),
13153
+ addressError && /* @__PURE__ */ jsxs46("div", { className: "uf-flex uf-items-center uf-gap-1.5 uf-mt-1.5", children: [
13154
+ /* @__PURE__ */ jsx53(AlertTriangle2, { className: "uf-w-3 uf-h-3", style: { color: colors2.error } }),
13155
+ /* @__PURE__ */ jsx53("span", { className: "uf-text-xs", style: { color: colors2.error, fontFamily: fonts.regular }, children: addressError })
13156
+ ] })
13157
+ ] }),
13158
+ /* @__PURE__ */ jsxs46("div", { children: [
13159
+ /* @__PURE__ */ jsxs46("div", { className: "uf-text-xs uf-mb-1.5", style: { color: components.card.labelColor, fontFamily: fonts.medium }, children: [
13160
+ t8.amount,
13161
+ minimumWithdrawAmountUsd != null && minimumWithdrawAmountUsd > 0 && /* @__PURE__ */ jsx53("span", { style: { color: colors2.warning, fontFamily: fonts.regular }, children: ` ($${minimumWithdrawAmountUsd.toFixed(2)} min)` })
13162
+ ] }),
13163
+ /* @__PURE__ */ jsx53(
13164
+ "style",
13165
+ {
13166
+ dangerouslySetInnerHTML: {
13167
+ __html: `.uf-withdraw-amt::placeholder { color: ${components.search.placeholderColor}; }`
13168
+ }
13169
+ }
13170
+ ),
13171
+ /* @__PURE__ */ jsxs46(
13172
+ "div",
13173
+ {
13174
+ className: "uf-flex uf-items-center uf-gap-2 uf-px-3 uf-py-2.5",
13175
+ style: {
13176
+ backgroundColor: components.search.backgroundColor,
13177
+ borderRadius: components.input.borderRadius,
13178
+ border: `${components.input.borderWidth}px solid ${components.input.borderColor}`
13179
+ },
13180
+ children: [
13181
+ /* @__PURE__ */ jsx53(
13182
+ "input",
13183
+ {
13184
+ type: "text",
13185
+ inputMode: "decimal",
13186
+ placeholder: "0.00",
13187
+ value: amount,
13188
+ onChange: (e) => {
13189
+ const val = e.target.value;
13190
+ if (val === "" || /^\d*\.?\d*$/.test(val)) {
13191
+ setAmount(val);
13192
+ setSubmitError(null);
13193
+ }
13194
+ },
13195
+ className: "uf-withdraw-amt uf-flex-1 uf-min-w-0 uf-bg-transparent uf-text-sm uf-outline-none",
13196
+ style: {
13197
+ color: components.search.inputColor,
13198
+ fontFamily: fonts.regular
13199
+ }
13200
+ }
13201
+ ),
13202
+ /* @__PURE__ */ jsx53("span", { className: "uf-text-sm uf-shrink-0", style: { color: colors2.foregroundMuted, fontFamily: fonts.medium }, children: inputUnit === "crypto" ? tokenSymbol : "USD" }),
13203
+ /* @__PURE__ */ jsx53(
13204
+ "button",
13205
+ {
13206
+ type: "button",
13207
+ onClick: handleMaxClick,
13208
+ 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",
13209
+ style: { backgroundColor: colors2.primary + "20", color: colors2.primary, fontFamily: fonts.medium },
13210
+ children: "Max"
13211
+ }
13212
+ )
13213
+ ]
13214
+ }
13215
+ ),
13216
+ /* @__PURE__ */ jsxs46("div", { className: "uf-flex uf-items-center uf-justify-between uf-mt-1.5 uf-px-3", children: [
13217
+ /* @__PURE__ */ jsxs46("div", { className: "uf-flex uf-items-center uf-gap-1", children: [
13218
+ /* @__PURE__ */ jsx53("span", { className: "uf-text-xs", style: { color: colors2.foregroundMuted, fontFamily: fonts.regular }, children: convertedDisplay || (inputUnit === "crypto" ? "$0.00" : `0.00 ${tokenSymbol}`) }),
13219
+ exchangeRate > 0 && /* @__PURE__ */ jsx53(
13220
+ "button",
13221
+ {
13222
+ type: "button",
13223
+ onClick: handleSwitchUnit,
13224
+ className: "uf-p-0.5 uf-rounded uf-transition-colors hover:uf-opacity-70",
13225
+ style: { color: colors2.foregroundMuted },
13226
+ title: "Switch unit",
13227
+ children: /* @__PURE__ */ jsx53(ArrowUpDown, { className: "uf-w-3 uf-h-3" })
13228
+ }
13229
+ )
13230
+ ] }),
13231
+ /* @__PURE__ */ jsxs46("div", { children: [
13232
+ balanceDisplay && /* @__PURE__ */ jsxs46("span", { className: "uf-text-xs", style: { color: colors2.foregroundMuted, fontFamily: fonts.regular }, children: [
13233
+ t8.balance,
13234
+ ": ",
13235
+ balanceDisplay
13236
+ ] }),
13237
+ isLoadingBalance && /* @__PURE__ */ jsx53("div", { className: "uf-h-3 uf-w-16 uf-bg-muted uf-rounded uf-animate-pulse" })
13238
+ ] })
13239
+ ] })
13240
+ ] }),
13241
+ /* @__PURE__ */ jsxs46("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: [
13242
+ /* @__PURE__ */ jsxs46(
13243
+ "button",
13244
+ {
13245
+ type: "button",
13246
+ onClick: () => setDetailsExpanded(!detailsExpanded),
13247
+ className: "uf-w-full uf-flex uf-items-center uf-justify-between uf-py-2.5",
13248
+ children: [
13249
+ /* @__PURE__ */ jsxs46("div", { className: "uf-flex uf-items-center uf-gap-2", children: [
13250
+ /* @__PURE__ */ jsx53("div", { className: "uf-rounded-full uf-p-1", style: { backgroundColor: components.card.iconBackgroundColor }, children: /* @__PURE__ */ jsx53(Clock4, { className: "uf-w-3 uf-h-3", style: { color: components.card.iconColor } }) }),
13251
+ /* @__PURE__ */ jsxs46("span", { className: "uf-text-xs", style: { color: components.card.labelColor, fontFamily: fonts.regular }, children: [
13252
+ tCrypto.processingTime.label,
13253
+ ":",
13254
+ " ",
13255
+ /* @__PURE__ */ jsx53("span", { style: { color: components.card.titleColor, fontFamily: fonts.medium }, children: formatProcessingTime2(estimatedProcessingTime) })
13256
+ ] })
13257
+ ] }),
13258
+ detailsExpanded ? /* @__PURE__ */ jsx53(ChevronUp6, { className: "uf-w-4 uf-h-4", style: { color: components.card.actionColor } }) : /* @__PURE__ */ jsx53(ChevronDown7, { className: "uf-w-4 uf-h-4", style: { color: components.card.actionColor } })
13259
+ ]
13260
+ }
13261
+ ),
13262
+ detailsExpanded && /* @__PURE__ */ jsxs46("div", { className: "uf-pb-3 uf-space-y-2.5", children: [
13263
+ /* @__PURE__ */ jsxs46("div", { className: "uf-flex uf-items-center uf-gap-2", children: [
13264
+ /* @__PURE__ */ jsx53("div", { className: "uf-rounded-full uf-p-1", style: { backgroundColor: components.card.iconBackgroundColor }, children: /* @__PURE__ */ jsx53(ShieldCheck3, { className: "uf-w-3 uf-h-3", style: { color: components.card.iconColor } }) }),
13265
+ /* @__PURE__ */ jsxs46("span", { className: "uf-text-xs", style: { color: components.card.labelColor, fontFamily: fonts.regular }, children: [
13266
+ tCrypto.slippage.label,
13267
+ ":",
13268
+ " ",
13269
+ /* @__PURE__ */ jsxs46("span", { style: { color: components.card.titleColor, fontFamily: fonts.medium }, children: [
13270
+ tCrypto.slippage.auto,
13271
+ " \u2022 ",
13272
+ (maxSlippagePercent ?? 0.25).toFixed(2),
13273
+ "%"
13274
+ ] })
13275
+ ] })
13276
+ ] }),
13277
+ /* @__PURE__ */ jsxs46("div", { className: "uf-flex uf-items-center uf-gap-2", children: [
13278
+ /* @__PURE__ */ jsx53("div", { className: "uf-rounded-full uf-p-1", style: { backgroundColor: components.card.iconBackgroundColor }, children: /* @__PURE__ */ jsx53(DollarSign3, { className: "uf-w-3 uf-h-3", style: { color: components.card.iconColor } }) }),
13279
+ /* @__PURE__ */ jsxs46("span", { className: "uf-text-xs", style: { color: components.card.labelColor, fontFamily: fonts.regular }, children: [
13280
+ tCrypto.priceImpact.label,
13281
+ ":",
13282
+ " ",
13283
+ /* @__PURE__ */ jsxs46("span", { style: { color: components.card.titleColor, fontFamily: fonts.medium }, children: [
13284
+ (priceImpactPercent ?? 0).toFixed(2),
13285
+ "%"
13286
+ ] })
13287
+ ] })
13288
+ ] })
13289
+ ] })
13290
+ ] }),
13291
+ !canWithdraw && !submitError && /* @__PURE__ */ jsxs46(
13292
+ "div",
13293
+ {
13294
+ className: "uf-flex uf-items-start uf-gap-2.5 uf-p-3 uf-rounded-xl",
13295
+ style: { backgroundColor: colors2.card, border: `1px solid ${colors2.border}` },
13296
+ children: [
13297
+ /* @__PURE__ */ jsx53(Wallet3, { className: "uf-w-4 uf-h-4 uf-flex-shrink-0 uf-mt-0.5", style: { color: colors2.warning } }),
13298
+ /* @__PURE__ */ jsx53("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." })
13299
+ ]
13300
+ }
13301
+ ),
13302
+ isWalletMatch && connectedWalletName ? /* @__PURE__ */ jsx53(
13303
+ "button",
13304
+ {
13305
+ type: "button",
13306
+ onClick: handleWithdraw,
13307
+ disabled: !isFormValid || !canWithdraw || isSubmitting || !selectedToken || !selectedChain,
13308
+ 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",
13309
+ style: {
13310
+ backgroundColor: colors2.primary,
13311
+ color: colors2.primaryForeground,
13312
+ fontFamily: fonts.medium,
13313
+ borderRadius: components.button.borderRadius,
13314
+ border: `${components.button.borderWidth}px solid ${components.button.borderColor}`
13315
+ },
13316
+ children: isSubmitting ? /* @__PURE__ */ jsxs46(Fragment10, { children: [
13317
+ /* @__PURE__ */ jsx53(Loader25, { className: "uf-w-4 uf-h-4 uf-animate-spin" }),
13318
+ "Processing..."
13319
+ ] }) : isOverBalance ? /* @__PURE__ */ jsx53(Fragment10, { children: "Insufficient balance" }) : isBelowMinimum ? /* @__PURE__ */ jsx53(Fragment10, { children: "Minimum amount not met" }) : submitError ? /* @__PURE__ */ jsx53(Fragment10, { children: "Withdrawal failed. Try again" }) : /* @__PURE__ */ jsxs46(Fragment10, { children: [
13320
+ /* @__PURE__ */ jsx53(Wallet3, { className: "uf-w-4 uf-h-4" }),
13321
+ "Withdraw from ",
13322
+ connectedWalletName
13323
+ ] })
13324
+ }
13325
+ ) : /* @__PURE__ */ jsx53(
13326
+ "button",
13327
+ {
13328
+ type: "button",
13329
+ onClick: handleWithdraw,
13330
+ disabled: !isFormValid || !canWithdraw || isSubmitting || !selectedToken || !selectedChain,
13331
+ 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",
13332
+ style: {
13333
+ backgroundColor: colors2.primary,
13334
+ color: colors2.primaryForeground,
13335
+ fontFamily: fonts.medium,
13336
+ borderRadius: components.button.borderRadius,
13337
+ border: `${components.button.borderWidth}px solid ${components.button.borderColor}`
13338
+ },
13339
+ children: isSubmitting ? /* @__PURE__ */ jsxs46("span", { className: "uf-flex uf-items-center uf-justify-center uf-gap-2", children: [
13340
+ /* @__PURE__ */ jsx53(Loader25, { className: "uf-w-4 uf-h-4 uf-animate-spin" }),
13341
+ "Processing..."
13342
+ ] }) : isOverBalance ? "Insufficient balance" : isBelowMinimum ? "Minimum amount not met" : submitError ? "Withdrawal failed. Try again" : t8.withdraw
13343
+ }
13344
+ ),
13345
+ /* @__PURE__ */ jsxs46("div", { className: "uf-flex uf-items-center uf-justify-between uf-text-xs uf-pt-1", children: [
13346
+ /* @__PURE__ */ jsx53("div", { children: footerLeft }),
13347
+ /* @__PURE__ */ jsx53(DepositFooterLinks, { onGlossaryClick: () => setGlossaryOpen(true) })
13348
+ ] }),
13349
+ /* @__PURE__ */ jsx53(
13350
+ GlossaryModal,
13351
+ {
13352
+ open: glossaryOpen,
13353
+ onOpenChange: setGlossaryOpen
13354
+ }
13355
+ )
13356
+ ] });
13357
+ }
13358
+
13359
+ // src/components/withdrawals/WithdrawExecutionItem.tsx
13360
+ import { ChevronRight as ChevronRight12 } from "lucide-react";
13361
+ import {
13362
+ ExecutionStatus as ExecutionStatus6,
13363
+ getIconUrl as getIconUrl5
13364
+ } from "@unifold/core";
13365
+ import { jsx as jsx54, jsxs as jsxs47 } from "react/jsx-runtime";
13366
+ function WithdrawExecutionItem({
13367
+ execution,
13368
+ onClick
13369
+ }) {
13370
+ const { colors: colors2, fonts, components } = useTheme();
13371
+ const isPending = execution.status === ExecutionStatus6.PENDING || execution.status === ExecutionStatus6.WAITING || execution.status === ExecutionStatus6.DELAYED;
13372
+ const formatDateTime = (timestamp) => {
13373
+ try {
13374
+ const date = new Date(timestamp);
13375
+ const monthDay = date.toLocaleDateString("en-US", {
13376
+ month: "short",
13377
+ day: "numeric",
13378
+ year: "numeric"
13379
+ });
13380
+ const time = date.toLocaleTimeString("en-US", {
13381
+ hour: "numeric",
13382
+ minute: "2-digit",
13383
+ hour12: true
13384
+ }).toLowerCase();
13385
+ return `${monthDay} at ${time}`;
13386
+ } catch {
13387
+ return timestamp;
13388
+ }
13389
+ };
13390
+ const formatUsdAmount2 = (sourceAmountUsd) => {
13391
+ try {
13392
+ const amount = Number(sourceAmountUsd);
13393
+ return new Intl.NumberFormat("en-US", {
13394
+ style: "currency",
13395
+ currency: "USD",
13396
+ minimumFractionDigits: 2,
13397
+ maximumFractionDigits: 2
13398
+ }).format(amount);
13399
+ } catch {
13400
+ return "$0.00";
13401
+ }
13402
+ };
13403
+ return /* @__PURE__ */ jsxs47(
13404
+ "button",
13405
+ {
13406
+ onClick,
13407
+ 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",
13408
+ style: {
13409
+ backgroundColor: components.card.backgroundColor,
13410
+ borderRadius: components.list.rowBorderRadius,
13411
+ border: `${components.card.borderWidth}px solid ${components.card.borderColor}`
13412
+ },
13413
+ children: [
13414
+ /* @__PURE__ */ jsxs47("div", { className: "uf-relative uf-flex-shrink-0 uf-w-9 uf-h-9", children: [
13415
+ /* @__PURE__ */ jsx54(
13416
+ "img",
13417
+ {
13418
+ src: execution.destination_token_metadata?.icon_url || getIconUrl5("/icons/tokens/svg/usdc.svg"),
13419
+ alt: "Token",
13420
+ width: 36,
13421
+ height: 36,
13422
+ loading: "lazy",
13423
+ className: "uf-rounded-full uf-w-9 uf-h-9"
13424
+ }
13425
+ ),
13426
+ isPending ? /* @__PURE__ */ jsx54(
13427
+ "div",
13428
+ {
13429
+ className: "uf-absolute -uf-bottom-0.5 -uf-right-0.5 uf-rounded-full uf-p-0.5",
13430
+ style: { backgroundColor: colors2.warning },
13431
+ children: /* @__PURE__ */ jsx54(
13432
+ "svg",
13433
+ {
13434
+ width: "10",
13435
+ height: "10",
13436
+ viewBox: "0 0 12 12",
13437
+ fill: "none",
13438
+ className: "uf-animate-spin uf-block",
13439
+ children: /* @__PURE__ */ jsx54(
13440
+ "path",
13441
+ {
13442
+ d: "M6 1V3M6 9V11M1 6H3M9 6H11M2.5 2.5L4 4M8 8L9.5 9.5M2.5 9.5L4 8M8 4L9.5 2.5",
13443
+ stroke: "white",
13444
+ strokeWidth: "2",
13445
+ strokeLinecap: "round"
13446
+ }
13447
+ )
13448
+ }
13449
+ )
13450
+ }
13451
+ ) : /* @__PURE__ */ jsx54(
13452
+ "div",
13453
+ {
13454
+ className: "uf-absolute -uf-bottom-0.5 -uf-right-0.5 uf-rounded-full uf-p-0.5",
13455
+ style: { backgroundColor: colors2.success },
13456
+ children: /* @__PURE__ */ jsx54(
13457
+ "svg",
13458
+ {
13459
+ width: "10",
13460
+ height: "10",
13461
+ viewBox: "0 0 12 12",
13462
+ fill: "none",
13463
+ className: "uf-block",
13464
+ children: /* @__PURE__ */ jsx54(
13465
+ "path",
13466
+ {
13467
+ d: "M10 3L4.5 8.5L2 6",
13468
+ stroke: "white",
13469
+ strokeWidth: "2",
13470
+ strokeLinecap: "round",
13471
+ strokeLinejoin: "round"
13472
+ }
13473
+ )
13474
+ }
13475
+ )
13476
+ }
13477
+ )
13478
+ ] }),
13479
+ /* @__PURE__ */ jsxs47("div", { className: "uf-flex-1 uf-min-w-0", children: [
13480
+ /* @__PURE__ */ jsx54(
13481
+ "h3",
13482
+ {
13483
+ className: "uf-font-medium uf-text-sm uf-leading-tight",
13484
+ style: {
13485
+ color: components.card.titleColor,
13486
+ fontFamily: fonts.medium
13487
+ },
13488
+ children: isPending ? "Withdrawal processing" : "Withdrawal completed"
13489
+ }
13490
+ ),
13491
+ /* @__PURE__ */ jsx54(
13492
+ "p",
13493
+ {
13494
+ className: "uf-text-xs uf-leading-tight",
13495
+ style: {
13496
+ color: components.card.subtitleColor,
13497
+ fontFamily: fonts.regular
13498
+ },
13499
+ children: formatDateTime(execution.created_at || (/* @__PURE__ */ new Date()).toISOString())
13500
+ }
13501
+ )
13502
+ ] }),
13503
+ /* @__PURE__ */ jsx54(
13504
+ "span",
13505
+ {
13506
+ className: "uf-font-medium uf-text-sm uf-flex-shrink-0",
13507
+ style: {
13508
+ color: components.card.textRightColor,
13509
+ fontFamily: fonts.medium
13510
+ },
13511
+ children: formatUsdAmount2(execution.source_amount_usd || "0")
13512
+ }
13513
+ ),
13514
+ /* @__PURE__ */ jsx54(
13515
+ ChevronRight12,
13516
+ {
13517
+ className: "uf-w-4 uf-h-4 uf-flex-shrink-0",
13518
+ style: { color: components.card.actionColor }
13519
+ }
13520
+ )
13521
+ ]
13522
+ }
13523
+ );
13524
+ }
13525
+
13526
+ // src/components/withdrawals/WithdrawConfirmingView.tsx
13527
+ import { useState as useState30, useEffect as useEffect24 } from "react";
13528
+ import { Fragment as Fragment11, jsx as jsx55, jsxs as jsxs48 } from "react/jsx-runtime";
13529
+ function truncateAddress4(addr) {
13530
+ if (addr.length <= 12) return addr;
13531
+ return `${addr.slice(0, 6)}...${addr.slice(-4)}`;
13532
+ }
13533
+ var SHOW_BUTTON_DELAY_MS = 5e3;
13534
+ function WithdrawConfirmingView({
13535
+ txInfo,
13536
+ executions,
13537
+ onClose,
13538
+ onViewTracker
13539
+ }) {
13540
+ const { colors: colors2, fonts, components } = useTheme();
13541
+ const [showButton, setShowButton] = useState30(false);
13542
+ const latestExecution = executions.length > 0 ? executions[executions.length - 1] : null;
13543
+ useEffect24(() => {
13544
+ if (latestExecution) return;
13545
+ const timer = setTimeout(() => setShowButton(true), SHOW_BUTTON_DELAY_MS);
13546
+ return () => clearTimeout(timer);
13547
+ }, [latestExecution]);
13548
+ const btnRadius = components.button.borderRadius;
13549
+ const btnBorder = `${components.button.borderWidth}px solid ${components.button.borderColor}`;
13550
+ if (latestExecution) {
13551
+ return /* @__PURE__ */ jsxs48(Fragment11, { children: [
13552
+ /* @__PURE__ */ jsx55(DepositHeader, { title: "Withdrawal Details", showClose: true, onClose }),
13553
+ /* @__PURE__ */ jsx55(DepositDetailContent, { execution: latestExecution, variant: "withdraw" }),
13554
+ /* @__PURE__ */ jsxs48("div", { className: "uf-flex uf-gap-2 uf-px-2 uf-pt-2", children: [
13555
+ /* @__PURE__ */ jsx55(
13556
+ "button",
13557
+ {
13558
+ type: "button",
13559
+ onClick: onViewTracker,
13560
+ className: "uf-flex-1 uf-py-2.5 uf-text-sm uf-transition-colors hover:uf-opacity-90",
13561
+ style: {
13562
+ backgroundColor: components.button.secondaryBackground,
13563
+ color: components.button.secondaryText,
13564
+ fontFamily: fonts.medium,
13565
+ borderRadius: btnRadius,
13566
+ border: btnBorder
13567
+ },
13568
+ children: "Withdrawal History"
13569
+ }
13570
+ ),
13571
+ /* @__PURE__ */ jsx55(
13572
+ "button",
13573
+ {
13574
+ type: "button",
13575
+ onClick: onClose,
13576
+ className: "uf-flex-1 uf-py-2.5 uf-text-sm uf-transition-colors hover:uf-opacity-90",
13577
+ style: {
13578
+ backgroundColor: components.button.primaryBackground,
13579
+ color: components.button.primaryText,
13580
+ fontFamily: fonts.medium,
13581
+ borderRadius: btnRadius,
13582
+ border: btnBorder
13583
+ },
13584
+ children: "Close"
13585
+ }
13586
+ )
13587
+ ] }),
13588
+ /* @__PURE__ */ jsx55("div", { className: "uf-pt-3", children: /* @__PURE__ */ jsx55(
13589
+ PoweredByUnifold,
13590
+ {
13591
+ color: colors2.foregroundMuted,
13592
+ className: "uf-flex uf-justify-center uf-shrink-0"
13593
+ }
13594
+ ) })
13595
+ ] });
13596
+ }
13597
+ return /* @__PURE__ */ jsxs48(Fragment11, { children: [
13598
+ /* @__PURE__ */ jsx55(DepositHeader, { title: "Withdrawal Status", showClose: true, onClose }),
13599
+ /* @__PURE__ */ jsxs48("div", { className: "uf-flex uf-flex-col uf-items-center uf-justify-center uf-py-16 uf-px-4", children: [
13600
+ /* @__PURE__ */ jsx55(
13601
+ "div",
13602
+ {
13603
+ className: "uf-w-20 uf-h-20 uf-rounded-full uf-flex uf-items-center uf-justify-center uf-mb-6",
13604
+ style: { backgroundColor: `${colors2.primary}20` },
13605
+ children: /* @__PURE__ */ jsx55(
13606
+ "svg",
13607
+ {
13608
+ width: "40",
13609
+ height: "40",
13610
+ viewBox: "0 0 24 24",
13611
+ fill: "none",
13612
+ className: "uf-animate-spin",
13613
+ children: /* @__PURE__ */ jsx55(
13614
+ "path",
13615
+ {
13616
+ d: "M21 12a9 9 0 1 1-6.22-8.56",
13617
+ stroke: colors2.primary,
13618
+ strokeWidth: "2.5",
13619
+ strokeLinecap: "round"
13620
+ }
13621
+ )
13622
+ }
13623
+ )
13624
+ }
13625
+ ),
13626
+ /* @__PURE__ */ jsx55(
13627
+ "h3",
13628
+ {
13629
+ className: "uf-text-xl uf-mb-2",
13630
+ style: { color: colors2.foreground, fontFamily: fonts.medium },
13631
+ children: "Checking Withdrawal"
13632
+ }
13633
+ ),
13634
+ /* @__PURE__ */ jsxs48(
13635
+ "p",
13636
+ {
13637
+ className: "uf-text-sm uf-text-center",
13638
+ style: { color: colors2.foregroundMuted, fontFamily: fonts.regular },
13639
+ children: [
13640
+ txInfo.amount,
13641
+ " ",
13642
+ txInfo.sourceTokenSymbol,
13643
+ " to",
13644
+ " ",
13645
+ truncateAddress4(txInfo.recipientAddress)
13646
+ ]
13647
+ }
13648
+ )
13649
+ ] }),
13650
+ showButton && /* @__PURE__ */ jsx55("div", { className: "uf-px-1 uf-pb-1", children: /* @__PURE__ */ jsx55(
13651
+ "button",
13652
+ {
13653
+ type: "button",
13654
+ onClick: onViewTracker,
13655
+ className: "uf-w-full uf-py-2.5 uf-text-sm uf-transition-colors hover:uf-opacity-90",
13656
+ style: {
13657
+ backgroundColor: components.button.secondaryBackground,
13658
+ color: components.button.secondaryText,
13659
+ fontFamily: fonts.medium,
13660
+ borderRadius: btnRadius,
13661
+ border: btnBorder
13662
+ },
13663
+ children: "Withdrawal History"
13664
+ }
13665
+ ) }),
13666
+ /* @__PURE__ */ jsx55("div", { className: "uf-pt-3", children: /* @__PURE__ */ jsx55(
13667
+ PoweredByUnifold,
13668
+ {
13669
+ color: colors2.foregroundMuted,
13670
+ className: "uf-flex uf-justify-center uf-shrink-0"
13671
+ }
13672
+ ) })
13673
+ ] });
13674
+ }
13675
+
13676
+ // src/components/withdrawals/WithdrawModal.tsx
13677
+ import {
13678
+ createDepositAddress as createDepositAddress2,
13679
+ getWalletByChainType as getWalletByChainType4,
13680
+ ActionType as ActionType6
13681
+ } from "@unifold/core";
13682
+ import { Fragment as Fragment12, jsx as jsx56, jsxs as jsxs49 } from "react/jsx-runtime";
13683
+ var t9 = i18n.withdrawModal;
13684
+ var getChainKey5 = (chainId, chainType) => `${chainType}:${chainId}`;
13685
+ function WithdrawModal({
13686
+ open,
13687
+ onOpenChange,
13688
+ publishableKey,
13689
+ modalTitle,
13690
+ externalUserId,
13691
+ sourceChainType,
13692
+ sourceChainId,
13693
+ sourceTokenAddress,
13694
+ sourceTokenSymbol,
13695
+ recipientAddress: recipientAddressProp,
13696
+ senderAddress,
13697
+ onWithdraw,
13698
+ onWithdrawSuccess,
13699
+ onWithdrawError,
13700
+ theme = "dark",
13701
+ hideOverlay = false
13702
+ }) {
13703
+ const { colors: colors2, fonts, components } = useTheme();
13704
+ const [containerEl, setContainerEl] = useState31(null);
13705
+ const containerCallbackRef = useCallback6((el) => {
13706
+ setContainerEl(el);
13707
+ }, []);
13708
+ const [resolvedTheme, setResolvedTheme] = useState31(
13709
+ theme === "auto" ? "dark" : theme
13710
+ );
13711
+ useEffect25(() => {
13712
+ if (theme === "auto") {
13713
+ const mq = window.matchMedia("(prefers-color-scheme: dark)");
13714
+ setResolvedTheme(mq.matches ? "dark" : "light");
13715
+ const h = (e) => setResolvedTheme(e.matches ? "dark" : "light");
13716
+ mq.addEventListener("change", h);
13717
+ return () => mq.removeEventListener("change", h);
13718
+ }
13719
+ setResolvedTheme(theme);
13720
+ }, [theme]);
13721
+ const themeClass = resolvedTheme === "dark" ? "uf-dark" : "";
13722
+ const { data: tokensResponse, isLoading: tokensLoading } = useSupportedDestinationTokens(publishableKey, open);
13723
+ const destinationTokens = tokensResponse?.data ?? [];
13724
+ const { data: sourceValidation, isLoading: isCheckingSourceToken } = useSourceTokenValidation({
13725
+ sourceChainType,
13726
+ sourceChainId,
13727
+ sourceTokenAddress,
13728
+ sourceTokenSymbol,
13729
+ publishableKey,
13730
+ enabled: open
13731
+ });
13732
+ const { data: balanceData, isLoading: isLoadingBalance } = useAddressBalance({
13733
+ address: senderAddress,
13734
+ chainType: sourceChainType,
13735
+ chainId: sourceChainId,
13736
+ tokenAddress: sourceTokenAddress,
13737
+ publishableKey,
13738
+ enabled: open
13739
+ });
13740
+ const [selectedToken, setSelectedToken] = useState31(null);
13741
+ const [selectedChain, setSelectedChain] = useState31(null);
13742
+ const [detectedWallet, setDetectedWallet] = useState31(null);
13743
+ const connectedWalletName = detectedWallet?.name ?? null;
13744
+ const isWalletMatch = !!detectedWallet;
13745
+ useEffect25(() => {
13746
+ if (!senderAddress || !open) {
13747
+ setDetectedWallet(null);
13748
+ return;
13749
+ }
13750
+ let cancelled = false;
13751
+ detectBrowserWallet(sourceChainType, senderAddress).then((wallet) => {
13752
+ if (!cancelled) setDetectedWallet(wallet);
13753
+ });
13754
+ return () => {
13755
+ cancelled = true;
13756
+ };
13757
+ }, [senderAddress, sourceChainType, open]);
13758
+ const [view, setView] = useState31("form");
13759
+ const [withdrawDepositWalletId, setWithdrawDepositWalletId] = useState31();
13760
+ const [selectedExecution, setSelectedExecution] = useState31(null);
13761
+ const [submittedTxInfo, setSubmittedTxInfo] = useState31(null);
13762
+ const { executions: realtimeExecutions } = useWithdrawPolling({
13763
+ userId: externalUserId,
13764
+ publishableKey,
13765
+ depositWalletId: withdrawDepositWalletId,
13766
+ enabled: !!withdrawDepositWalletId && open,
13767
+ onWithdrawSuccess: onWithdrawSuccess ? (d) => onWithdrawSuccess({ message: d.message, transaction: d.transaction }) : void 0,
13768
+ onWithdrawError
13769
+ });
13770
+ const { data: allWithdrawalsData } = useExecutions(externalUserId, publishableKey, {
13771
+ actionType: ActionType6.Withdraw,
13772
+ enabled: open,
13773
+ refetchInterval: view === "tracker" || view === "detail" ? 5e3 : 15e3
13774
+ });
13775
+ const allWithdrawals = allWithdrawalsData?.data ?? [];
13776
+ const handleDepositWalletCreation = useCallback6(async (params) => {
13777
+ const { data: wallets } = await createDepositAddress2(
13778
+ {
13779
+ external_user_id: externalUserId,
13780
+ destination_chain_type: params.destinationChainType,
13781
+ destination_chain_id: params.destinationChainId,
13782
+ destination_token_address: params.destinationTokenAddress,
13783
+ recipient_address: params.recipientAddress,
13784
+ action_type: ActionType6.Withdraw
13785
+ },
13786
+ publishableKey
13787
+ );
13788
+ const depositWallet = getWalletByChainType4(wallets, sourceChainType);
13789
+ if (!depositWallet) {
13790
+ throw new Error(`No deposit wallet available for ${sourceChainType}`);
13791
+ }
13792
+ setWithdrawDepositWalletId(depositWallet.id);
13793
+ return depositWallet;
13794
+ }, [externalUserId, publishableKey, sourceChainType]);
13795
+ const handleWithdrawSubmitted = useCallback6((txInfo) => {
13796
+ setSubmittedTxInfo(txInfo);
13797
+ setView("confirming");
13798
+ }, []);
13799
+ useEffect25(() => {
13800
+ if (!destinationTokens.length || selectedToken) return;
13801
+ const first = destinationTokens[0];
13802
+ if (first?.chains.length > 0) {
13803
+ setSelectedToken(first);
13804
+ setSelectedChain(first.chains[0]);
13805
+ }
13806
+ }, [destinationTokens, selectedToken]);
13807
+ const resetViewTimeoutRef = useRef8(null);
13808
+ const handleClose = useCallback6(() => {
13809
+ onOpenChange(false);
13810
+ if (resetViewTimeoutRef.current) clearTimeout(resetViewTimeoutRef.current);
13811
+ resetViewTimeoutRef.current = setTimeout(() => {
13812
+ setSelectedToken(null);
13813
+ setSelectedChain(null);
13814
+ setView("form");
13815
+ setSelectedExecution(null);
13816
+ setSubmittedTxInfo(null);
13817
+ setWithdrawDepositWalletId(void 0);
13818
+ resetViewTimeoutRef.current = null;
13819
+ }, 200);
13820
+ }, [onOpenChange]);
13821
+ useLayoutEffect3(() => {
13822
+ if (!open) return;
13823
+ if (resetViewTimeoutRef.current) {
13824
+ clearTimeout(resetViewTimeoutRef.current);
13825
+ resetViewTimeoutRef.current = null;
13826
+ }
13827
+ setSelectedToken(null);
13828
+ setSelectedChain(null);
13829
+ setView("form");
13830
+ setSelectedExecution(null);
13831
+ setSubmittedTxInfo(null);
13832
+ setWithdrawDepositWalletId(void 0);
13833
+ }, [open]);
13834
+ useEffect25(() => () => {
13835
+ if (resetViewTimeoutRef.current) clearTimeout(resetViewTimeoutRef.current);
13836
+ }, []);
13837
+ const handleTokenSymbolChange = useCallback6((symbol) => {
13838
+ const tok = destinationTokens.find((t11) => t11.symbol === symbol);
13839
+ if (tok) {
13840
+ setSelectedToken(tok);
13841
+ if (tok.chains.length > 0) setSelectedChain(tok.chains[0]);
13842
+ }
13843
+ }, [destinationTokens]);
13844
+ const handleChainKeyChange = useCallback6((chainKey) => {
13845
+ if (!selectedToken) return;
13846
+ const chain = selectedToken.chains.find((c) => getChainKey5(c.chain_id, c.chain_type) === chainKey);
13847
+ if (chain) setSelectedChain(chain);
13848
+ }, [selectedToken]);
13849
+ const isSourceSupported = sourceValidation?.isSupported ?? null;
13850
+ const canWithdraw = !!onWithdraw || isWalletMatch;
13851
+ const isAnyLoading = tokensLoading || isCheckingSourceToken;
13852
+ const withdrawPoweredByFooter = /* @__PURE__ */ jsx56("div", { className: "uf-pt-3", children: /* @__PURE__ */ jsx56(PoweredByUnifold, { color: colors2.foregroundMuted, className: "uf-flex uf-justify-center uf-shrink-0" }) });
13853
+ return /* @__PURE__ */ jsx56(PortalContainerProvider, { value: hideOverlay ? containerEl : null, children: /* @__PURE__ */ jsx56(Dialog, { open: hideOverlay || open, onOpenChange: hideOverlay ? void 0 : handleClose, modal: !hideOverlay, children: /* @__PURE__ */ jsx56(
13854
+ DialogContent,
13855
+ {
13856
+ ref: hideOverlay ? containerCallbackRef : void 0,
13857
+ hideOverlay,
13858
+ 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}`}`,
13859
+ style: { backgroundColor: colors2.background },
13860
+ onPointerDownOutside: (e) => e.preventDefault(),
13861
+ onInteractOutside: (e) => e.preventDefault(),
13862
+ children: /* @__PURE__ */ jsx56(ThemeStyleInjector, { children: view === "confirming" && submittedTxInfo ? /* @__PURE__ */ jsx56(
13863
+ WithdrawConfirmingView,
13864
+ {
13865
+ txInfo: submittedTxInfo,
13866
+ executions: realtimeExecutions,
13867
+ onClose: handleClose,
13868
+ onViewTracker: () => setView("tracker")
13869
+ }
13870
+ ) : view === "detail" && selectedExecution ? /* @__PURE__ */ jsxs49(Fragment12, { children: [
13871
+ /* @__PURE__ */ jsx56(DepositHeader, { title: "Withdrawal Details", showBack: true, showClose: !hideOverlay, onBack: () => {
13872
+ setSelectedExecution(null);
13873
+ setView("tracker");
13874
+ }, onClose: handleClose }),
13875
+ /* @__PURE__ */ jsx56(DepositDetailContent, { execution: selectedExecution, variant: "withdraw" }),
13876
+ withdrawPoweredByFooter
13877
+ ] }) : view === "tracker" ? (
13878
+ /* ---------- Tracker view: execution list ---------- */
13879
+ /* @__PURE__ */ jsxs49(Fragment12, { children: [
13880
+ /* @__PURE__ */ jsx56(DepositHeader, { title: "Withdrawal History", showBack: true, showClose: !hideOverlay, onBack: () => setView("form"), onClose: handleClose }),
13881
+ /* @__PURE__ */ jsx56("div", { className: "uf-flex uf-flex-col uf-gap-2", style: { minHeight: 200 }, children: allWithdrawals.length === 0 ? /* @__PURE__ */ jsx56("div", { className: "uf-flex uf-items-center uf-justify-center uf-py-8", children: /* @__PURE__ */ jsx56("p", { className: "uf-text-sm", style: { color: colors2.foregroundMuted, fontFamily: fonts.regular }, children: "No withdrawals to track yet" }) }) : allWithdrawals.map((ex) => /* @__PURE__ */ jsx56(
13882
+ WithdrawExecutionItem,
13883
+ {
13884
+ execution: ex,
13885
+ onClick: () => {
13886
+ setSelectedExecution(ex);
13887
+ setView("detail");
13888
+ }
13889
+ },
13890
+ ex.id
13891
+ )) }),
13892
+ withdrawPoweredByFooter
13893
+ ] })
13894
+ ) : (
13895
+ /* ---------- Form view (default) ---------- */
13896
+ /* @__PURE__ */ jsxs49(Fragment12, { children: [
13897
+ /* @__PURE__ */ jsx56(DepositHeader, { title: modalTitle || t9.title, showClose: !hideOverlay, onClose: handleClose }),
13898
+ /* @__PURE__ */ jsxs49("div", { className: "uf-flex uf-flex-col uf-gap-3", children: [
13899
+ isAnyLoading ? /* @__PURE__ */ jsx56("div", { className: "uf-space-y-3", children: [1, 2, 3].map((i) => /* @__PURE__ */ jsx56("div", { className: "uf-w-full uf-bg-secondary uf-rounded-xl uf-p-3 uf-flex uf-items-center uf-animate-pulse", children: /* @__PURE__ */ jsx56("div", { className: "uf-bg-muted uf-rounded-lg uf-w-full uf-h-10" }) }, i)) }) : isSourceSupported === false ? /* @__PURE__ */ jsxs49("div", { className: "uf-flex uf-flex-col uf-items-center uf-justify-center uf-py-8 uf-px-4 uf-text-center", children: [
13900
+ /* @__PURE__ */ jsx56("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__ */ jsx56(AlertTriangle3, { className: "uf-w-8 uf-h-8 uf-text-muted-foreground" }) }),
13901
+ /* @__PURE__ */ jsx56("h3", { className: "uf-text-lg uf-font-semibold uf-mb-2", style: { color: colors2.foreground, fontFamily: fonts.medium }, children: "Unsupported Source Token" }),
13902
+ /* @__PURE__ */ jsx56("p", { className: "uf-text-sm uf-max-w-[280px]", style: { color: colors2.foregroundMuted, fontFamily: fonts.regular }, children: sourceValidation?.errorMessage })
13903
+ ] }) : /* @__PURE__ */ jsxs49(Fragment12, { children: [
13904
+ /* @__PURE__ */ jsx56(
13905
+ WithdrawDoubleInput,
13906
+ {
13907
+ tokens: destinationTokens,
13908
+ selectedTokenSymbol: selectedToken?.symbol ?? null,
13909
+ selectedChainKey: selectedChain ? getChainKey5(selectedChain.chain_id, selectedChain.chain_type) : null,
13910
+ onTokenChange: handleTokenSymbolChange,
13911
+ onChainChange: handleChainKeyChange,
13912
+ isLoading: tokensLoading
13913
+ }
13914
+ ),
13915
+ /* @__PURE__ */ jsx56(
13916
+ WithdrawForm,
13917
+ {
13918
+ publishableKey,
13919
+ externalUserId,
13920
+ sourceChainType,
13921
+ selectedToken,
13922
+ selectedChain,
13923
+ sourceTokenSymbol,
13924
+ recipientAddressProp,
13925
+ balanceData: balanceData ?? null,
13926
+ isLoadingBalance,
13927
+ minimumWithdrawAmountUsd: sourceValidation?.minimumAmountUsd ?? null,
13928
+ estimatedProcessingTime: sourceValidation?.estimatedProcessingTime ?? null,
13929
+ maxSlippagePercent: sourceValidation?.maxSlippagePercent ?? null,
13930
+ priceImpactPercent: sourceValidation?.priceImpactPercent ?? null,
13931
+ detectedWallet,
13932
+ sourceChainId,
13933
+ sourceTokenAddress,
13934
+ isWalletMatch,
13935
+ connectedWalletName,
13936
+ canWithdraw,
13937
+ onWithdraw,
13938
+ onWithdrawError,
13939
+ onDepositWalletCreation: handleDepositWalletCreation,
13940
+ onWithdrawSubmitted: handleWithdrawSubmitted,
13941
+ footerLeft: /* @__PURE__ */ jsxs49(
13942
+ "button",
13943
+ {
13944
+ onClick: () => setView("tracker"),
13945
+ className: "uf-flex uf-items-center uf-gap-1 uf-transition-colors hover:uf-opacity-70",
13946
+ style: { color: colors2.foregroundMuted },
13947
+ children: [
13948
+ /* @__PURE__ */ jsx56(Clock5, { className: "uf-w-3.5 uf-h-3.5" }),
13949
+ "Withdrawal History",
13950
+ /* @__PURE__ */ jsx56(ChevronRight13, { className: "uf-w-3 uf-h-3" })
13951
+ ]
13952
+ }
13953
+ )
13954
+ }
13955
+ )
13956
+ ] }),
13957
+ withdrawPoweredByFooter
13958
+ ] })
13959
+ ] })
13960
+ ) })
13961
+ }
13962
+ ) }) });
13963
+ }
13964
+
13965
+ // src/components/withdrawals/WithdrawTokenSelector.tsx
13966
+ import { useState as useState32, useMemo as useMemo11 } from "react";
13967
+ import { Search } from "lucide-react";
13968
+ import { jsx as jsx57, jsxs as jsxs50 } from "react/jsx-runtime";
13969
+ var t10 = i18n.withdrawModal;
13970
+ function WithdrawTokenSelector({
13971
+ tokens,
13972
+ onSelect,
13973
+ onBack
13974
+ }) {
13975
+ const { themeClass, colors: colors2, fonts, components } = useTheme();
13976
+ const [searchQuery, setSearchQuery] = useState32("");
13977
+ const [hoveredKey, setHoveredKey] = useState32(null);
13978
+ const allOptions = useMemo11(() => {
13979
+ const options = [];
13980
+ tokens.forEach((token) => {
13981
+ token.chains.forEach((chain) => {
13982
+ options.push({ token, chain });
13983
+ });
13984
+ });
13985
+ return options;
13986
+ }, [tokens]);
13987
+ const filteredOptions = useMemo11(() => {
13988
+ if (!searchQuery.trim()) return allOptions;
13989
+ const query = searchQuery.toLowerCase();
13990
+ return allOptions.filter(
13991
+ ({ token, chain }) => token.symbol.toLowerCase().includes(query) || token.name.toLowerCase().includes(query) || chain.chain_name.toLowerCase().includes(query)
13992
+ );
13993
+ }, [allOptions, searchQuery]);
13994
+ return /* @__PURE__ */ jsxs50(
13995
+ "div",
13996
+ {
13997
+ className: "uf-flex uf-flex-col",
13998
+ style: { minHeight: 0, flex: 1 },
13999
+ children: [
14000
+ /* @__PURE__ */ jsxs50("div", { className: "uf-pb-3", children: [
14001
+ /* @__PURE__ */ jsx57(
14002
+ "style",
14003
+ {
14004
+ dangerouslySetInnerHTML: {
14005
+ __html: `.uf-withdraw-token-search::placeholder { color: ${components.search.placeholderColor}; }`
14006
+ }
14007
+ }
14008
+ ),
14009
+ /* @__PURE__ */ jsxs50("div", { style: { position: "relative" }, children: [
14010
+ /* @__PURE__ */ jsx57(
14011
+ Search,
14012
+ {
14013
+ className: "uf-absolute uf-left-3 uf-top-1/2 uf--translate-y-1/2 uf-w-4 uf-h-4",
14014
+ style: { color: components.search.placeholderColor }
14015
+ }
14016
+ ),
14017
+ /* @__PURE__ */ jsx57(
14018
+ "input",
14019
+ {
14020
+ type: "text",
14021
+ placeholder: "Search token or network",
14022
+ value: searchQuery,
14023
+ onChange: (e) => setSearchQuery(e.target.value),
14024
+ 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",
14025
+ style: {
14026
+ backgroundColor: components.search.backgroundColor,
14027
+ color: components.search.inputColor,
14028
+ fontFamily: fonts.regular,
14029
+ borderRadius: components.input.borderRadius,
14030
+ border: `${components.input.borderWidth}px solid ${components.input.borderColor}`
14031
+ }
14032
+ }
14033
+ )
14034
+ ] })
14035
+ ] }),
14036
+ /* @__PURE__ */ jsx57(
14037
+ "div",
14038
+ {
14039
+ className: "uf-text-xs uf-mb-2",
14040
+ style: {
14041
+ color: components.list.titleSectionColor,
14042
+ fontFamily: fonts.medium
14043
+ },
14044
+ children: t10.selectToken
14045
+ }
14046
+ ),
14047
+ /* @__PURE__ */ jsx57(
14048
+ "div",
14049
+ {
14050
+ className: "uf-flex-1 uf-overflow-y-auto uf-min-h-0 uf--mx-6 uf-px-6 uf-pb-3",
14051
+ style: { scrollbarWidth: "none" },
14052
+ children: filteredOptions.length === 0 ? /* @__PURE__ */ jsx57(
14053
+ "div",
14054
+ {
14055
+ style: {
14056
+ textAlign: "center",
14057
+ padding: "2rem 0",
14058
+ fontSize: 14,
14059
+ color: components.container.subtitleColor,
14060
+ fontFamily: fonts.regular
14061
+ },
14062
+ children: t10.noTokensAvailable
14063
+ }
14064
+ ) : /* @__PURE__ */ jsx57("div", { style: { display: "flex", flexDirection: "column", gap: 4 }, children: filteredOptions.map(({ token, chain }) => {
14065
+ const key = `${token.symbol}-${chain.chain_type}:${chain.chain_id}`;
14066
+ return /* @__PURE__ */ jsxs50(
14067
+ "button",
14068
+ {
14069
+ type: "button",
14070
+ onClick: () => onSelect(token, chain),
14071
+ onMouseEnter: () => setHoveredKey(key),
14072
+ onMouseLeave: () => setHoveredKey(null),
14073
+ className: "uf-transition-colors",
14074
+ style: {
14075
+ width: "100%",
14076
+ display: "flex",
14077
+ alignItems: "center",
14078
+ gap: 12,
14079
+ padding: 12,
14080
+ borderRadius: 12,
14081
+ border: "none",
14082
+ cursor: "pointer",
14083
+ textAlign: "left",
14084
+ backgroundColor: hoveredKey === key ? colors2.cardHover : "transparent"
14085
+ },
14086
+ children: [
14087
+ /* @__PURE__ */ jsxs50("div", { style: { position: "relative", flexShrink: 0 }, children: [
14088
+ /* @__PURE__ */ jsx57(
14089
+ "img",
14090
+ {
14091
+ src: token.icon_url,
14092
+ alt: token.symbol,
14093
+ width: 40,
14094
+ height: 40,
14095
+ loading: "lazy",
14096
+ className: "uf-rounded-full"
14097
+ }
14098
+ ),
14099
+ /* @__PURE__ */ jsx57(
14100
+ "div",
14101
+ {
14102
+ style: {
14103
+ position: "absolute",
14104
+ bottom: -4,
14105
+ right: -4
14106
+ },
14107
+ children: /* @__PURE__ */ jsx57(
14108
+ "img",
14109
+ {
14110
+ src: chain.icon_url,
14111
+ alt: chain.chain_name,
14112
+ width: 20,
14113
+ height: 20,
14114
+ loading: "lazy",
14115
+ className: "uf-rounded-full uf-border-2"
14116
+ }
14117
+ )
14118
+ }
14119
+ )
14120
+ ] }),
14121
+ /* @__PURE__ */ jsxs50("div", { style: { flex: 1, minWidth: 0 }, children: [
14122
+ /* @__PURE__ */ jsx57(
14123
+ "div",
14124
+ {
14125
+ style: {
14126
+ fontSize: 14,
14127
+ fontWeight: 500,
14128
+ color: components.card.titleColor,
14129
+ fontFamily: fonts.medium
14130
+ },
14131
+ children: token.symbol
14132
+ }
14133
+ ),
14134
+ /* @__PURE__ */ jsxs50(
14135
+ "div",
14136
+ {
14137
+ style: {
14138
+ fontSize: 12,
14139
+ color: components.card.subtitleColor,
14140
+ fontFamily: fonts.regular
14141
+ },
14142
+ children: [
14143
+ token.name,
14144
+ " \u2022 ",
14145
+ chain.chain_name
14146
+ ]
14147
+ }
14148
+ )
14149
+ ] })
14150
+ ]
14151
+ },
14152
+ key
14153
+ );
14154
+ }) })
14155
+ }
14156
+ ),
14157
+ /* @__PURE__ */ jsx57("div", { className: "uf-pt-3 uf-pb-2 uf-shrink-0", children: /* @__PURE__ */ jsx57(
14158
+ PoweredByUnifold,
14159
+ {
14160
+ color: colors2.foregroundMuted,
14161
+ className: "uf-flex uf-justify-center uf-shrink-0"
14162
+ }
14163
+ ) })
14164
+ ]
14165
+ }
14166
+ );
14167
+ }
12133
14168
  export {
12134
14169
  Button,
12135
14170
  BuyWithCard,
@@ -12175,15 +14210,29 @@ export {
12175
14210
  TransferCryptoButton,
12176
14211
  TransferCryptoDoubleInput,
12177
14212
  TransferCryptoSingleInput,
14213
+ WithdrawConfirmingView,
14214
+ WithdrawDoubleInput,
14215
+ WithdrawExecutionItem,
14216
+ WithdrawForm,
14217
+ WithdrawModal,
14218
+ WithdrawTokenSelector,
12178
14219
  buttonVariants,
12179
14220
  cn,
12180
14221
  colors,
12181
14222
  defaultColors,
14223
+ detectBrowserWallet,
12182
14224
  getColors,
12183
14225
  mergeColors,
12184
14226
  resolveComponentTokens,
14227
+ sendEvmWithdraw,
14228
+ sendSolanaWithdraw,
12185
14229
  truncateAddress,
14230
+ useAddressBalance,
12186
14231
  useAllowedCountry,
12187
14232
  useDepositPolling,
12188
- useTheme
14233
+ useSourceTokenValidation,
14234
+ useSupportedDestinationTokens,
14235
+ useTheme,
14236
+ useVerifyRecipientAddress,
14237
+ useWithdrawPolling
12189
14238
  };