@unifold/ui-react 0.1.21 → 0.1.23

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
@@ -1,5 +1,5 @@
1
1
  // src/components/deposits/DepositModal.tsx
2
- import { useState as useState16, useEffect as useEffect13 } from "react";
2
+ import { useState as useState17, useEffect as useEffect14 } from "react";
3
3
  import { ChevronRight as ChevronRight8, MapPinOff, AlertTriangle } from "lucide-react";
4
4
 
5
5
  // src/components/shared/dialog.tsx
@@ -13,6 +13,35 @@ import { twMerge } from "tailwind-merge";
13
13
  function cn(...inputs) {
14
14
  return twMerge(clsx(inputs));
15
15
  }
16
+ function truncateAddress(address, startChars = 10, endChars = 8) {
17
+ if (!address) return "";
18
+ const totalChars = startChars + endChars + 3;
19
+ if (address.length <= totalChars) return address;
20
+ return `${address.slice(0, startChars)}...${address.slice(-endChars)}`;
21
+ }
22
+ function formatEstimatedTime(seconds) {
23
+ if (seconds == null) {
24
+ return "< 1 min";
25
+ }
26
+ if (seconds < 60) {
27
+ return `< ${seconds} sec${seconds > 1 ? "s" : ""}`;
28
+ } else if (seconds < 3600) {
29
+ const mins = Math.ceil(seconds / 60);
30
+ return `< ${mins} min${mins > 1 ? "s" : ""}`;
31
+ } else {
32
+ let hrs = Math.floor(seconds / 3600);
33
+ let mins = Math.ceil(seconds % 3600 / 60);
34
+ if (mins === 60) {
35
+ hrs += 1;
36
+ mins = 0;
37
+ }
38
+ const hrLabel = hrs > 1 ? "hrs" : "hr";
39
+ if (mins === 0) {
40
+ return `< ${hrs} ${hrLabel}`;
41
+ }
42
+ return `< ${hrs} ${hrLabel} ${mins} min${mins > 1 ? "s" : ""}`;
43
+ }
44
+ }
16
45
 
17
46
  // src/context/ThemeContext.tsx
18
47
  import * as React from "react";
@@ -466,7 +495,7 @@ var DialogDescription = React2.forwardRef(({ className, ...props }, ref) => /* @
466
495
  DialogDescription.displayName = DialogPrimitive.Description.displayName;
467
496
 
468
497
  // src/components/deposits/BuyWithCard.tsx
469
- import { useState as useState8, useEffect as useEffect4, useRef as useRef2 } from "react";
498
+ import { useState as useState9, useEffect as useEffect5, useRef as useRef2 } from "react";
470
499
  import { ChevronDown as ChevronDown2, ChevronRight } from "lucide-react";
471
500
  import {
472
501
  getOnrampQuotes,
@@ -481,10 +510,12 @@ import {
481
510
  } from "@unifold/core";
482
511
 
483
512
  // src/components/deposits/CurrencyModal.tsx
484
- import { useState as useState3 } from "react";
513
+ import { useState as useState4 } from "react";
485
514
 
486
515
  // src/components/deposits/DepositHeader.tsx
487
516
  import { ArrowLeft, X as X2 } from "lucide-react";
517
+ import { useEffect as useEffect2, useState as useState2 } from "react";
518
+ import { getAddressBalance } from "@unifold/core";
488
519
  import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
489
520
  function DepositHeader({
490
521
  title,
@@ -492,10 +523,83 @@ function DepositHeader({
492
523
  showClose = true,
493
524
  onBack,
494
525
  onClose,
495
- badge
526
+ badge,
527
+ showBalance = false,
528
+ balanceAddress,
529
+ balanceChainType,
530
+ balanceChainId,
531
+ balanceTokenAddress,
532
+ publishableKey
496
533
  }) {
497
534
  const { colors: colors2, fonts, components } = useTheme();
498
- return /* @__PURE__ */ jsxs2("div", { className: "uf-flex uf-items-center uf-justify-between uf-pb-6", children: [
535
+ const [balance, setBalance] = useState2(null);
536
+ const [isLoadingBalance, setIsLoadingBalance] = useState2(false);
537
+ useEffect2(() => {
538
+ if (!showBalance || !balanceAddress || !balanceChainType || !balanceChainId || !balanceTokenAddress || !publishableKey) {
539
+ setBalance(null);
540
+ setIsLoadingBalance(false);
541
+ return;
542
+ }
543
+ let cancelled = false;
544
+ setIsLoadingBalance(true);
545
+ getAddressBalance(
546
+ balanceAddress,
547
+ balanceChainType,
548
+ balanceChainId,
549
+ balanceTokenAddress,
550
+ publishableKey
551
+ ).then((response) => {
552
+ if (cancelled) return;
553
+ if (response.balance && response.balance.amount !== "0") {
554
+ const value = Number(response.balance.amount) / 10 ** response.balance.token.decimals;
555
+ let formatted;
556
+ let maxDecimals = 4;
557
+ const symbol = response.balance.token.symbol?.toUpperCase() || "";
558
+ if (symbol === "BTC" || symbol === "WBTC") {
559
+ maxDecimals = 8;
560
+ } else if (symbol === "ETH" || symbol === "WETH") {
561
+ maxDecimals = 6;
562
+ }
563
+ if (value >= 1) {
564
+ formatted = value.toLocaleString(void 0, {
565
+ minimumFractionDigits: 2,
566
+ maximumFractionDigits: maxDecimals
567
+ });
568
+ } else if (value > 0) {
569
+ formatted = value.toLocaleString(void 0, {
570
+ minimumFractionDigits: 2,
571
+ maximumFractionDigits: maxDecimals,
572
+ minimumSignificantDigits: 2,
573
+ maximumSignificantDigits: 6
574
+ });
575
+ } else {
576
+ formatted = value.toExponential(2);
577
+ }
578
+ const balanceText = response.balance.amount_usd ? `Balance: $${response.balance.amount_usd} (${formatted} ${response.balance.token.symbol})` : `Balance: ${formatted} ${response.balance.token.symbol}`;
579
+ setBalance(balanceText);
580
+ } else {
581
+ setBalance(null);
582
+ }
583
+ }).catch((error) => {
584
+ if (cancelled) return;
585
+ console.error("Error fetching balance:", error);
586
+ setBalance(null);
587
+ }).finally(() => {
588
+ if (cancelled) return;
589
+ setIsLoadingBalance(false);
590
+ });
591
+ return () => {
592
+ cancelled = true;
593
+ };
594
+ }, [
595
+ showBalance,
596
+ balanceAddress,
597
+ balanceChainType,
598
+ balanceChainId,
599
+ balanceTokenAddress,
600
+ publishableKey
601
+ ]);
602
+ return /* @__PURE__ */ jsx3("div", { children: /* @__PURE__ */ jsxs2("div", { className: "uf-flex uf-items-center uf-justify-between uf-pb-6", children: [
499
603
  showBack ? /* @__PURE__ */ jsx3(
500
604
  "button",
501
605
  {
@@ -505,8 +609,32 @@ function DepositHeader({
505
609
  children: /* @__PURE__ */ jsx3(ArrowLeft, { className: "uf-w-5 uf-h-5" })
506
610
  }
507
611
  ) : /* @__PURE__ */ jsx3("div", { className: "uf-w-5 uf-h-5 uf-invisible" }),
508
- badge ? /* @__PURE__ */ jsxs2("div", { className: "uf-flex uf-items-center uf-gap-2", children: [
509
- /* @__PURE__ */ jsx3(
612
+ /* @__PURE__ */ jsxs2("div", { className: "uf-flex uf-flex-col uf-items-center", children: [
613
+ badge ? /* @__PURE__ */ jsxs2("div", { className: "uf-flex uf-items-center uf-gap-2", children: [
614
+ /* @__PURE__ */ jsx3(
615
+ DialogTitle,
616
+ {
617
+ className: "uf-text-center uf-text-base",
618
+ style: {
619
+ color: components.header.titleColor,
620
+ fontFamily: fonts.medium
621
+ },
622
+ children: title
623
+ }
624
+ ),
625
+ /* @__PURE__ */ jsx3(
626
+ "div",
627
+ {
628
+ className: "uf-px-2 uf-py-0.5 uf-rounded-full uf-text-[10px]",
629
+ style: {
630
+ backgroundColor: colors2.card,
631
+ color: colors2.foregroundMuted,
632
+ fontFamily: fonts.regular
633
+ },
634
+ children: badge.count
635
+ }
636
+ )
637
+ ] }) : /* @__PURE__ */ jsx3(
510
638
  DialogTitle,
511
639
  {
512
640
  className: "uf-text-center uf-text-base",
@@ -517,29 +645,19 @@ function DepositHeader({
517
645
  children: title
518
646
  }
519
647
  ),
520
- /* @__PURE__ */ jsx3(
648
+ showBalance && (isLoadingBalance ? /* @__PURE__ */ jsx3("div", { className: "uf-h-3 uf-w-32 uf-bg-muted uf-rounded uf-animate-pulse uf-mt-2" }) : balance ? /* @__PURE__ */ jsx3(
521
649
  "div",
522
650
  {
523
- className: "uf-px-2 uf-py-0.5 uf-rounded-full uf-text-[10px]",
651
+ className: "uf-text-xs uf-mt-2",
524
652
  style: {
525
- backgroundColor: colors2.card,
526
- color: colors2.foregroundMuted,
527
- fontFamily: fonts.regular
653
+ color: colors2.foreground,
654
+ fontFamily: fonts.regular,
655
+ opacity: 0.7
528
656
  },
529
- children: badge.count
657
+ children: balance
530
658
  }
531
- )
532
- ] }) : /* @__PURE__ */ jsx3(
533
- DialogTitle,
534
- {
535
- className: "uf-text-center uf-text-base",
536
- style: {
537
- color: components.header.titleColor,
538
- fontFamily: fonts.medium
539
- },
540
- children: title
541
- }
542
- ),
659
+ ) : null)
660
+ ] }),
543
661
  showClose ? /* @__PURE__ */ jsx3(
544
662
  "button",
545
663
  {
@@ -549,7 +667,7 @@ function DepositHeader({
549
667
  children: /* @__PURE__ */ jsx3(X2, { className: "uf-w-5 uf-h-5" })
550
668
  }
551
669
  ) : /* @__PURE__ */ jsx3("div", { className: "uf-w-5 uf-h-5 uf-invisible" })
552
- ] });
670
+ ] }) });
553
671
  }
554
672
 
555
673
  // src/components/currency/CurrencyListItem.tsx
@@ -662,7 +780,7 @@ function CurrencyModal({
662
780
  themeClass = ""
663
781
  }) {
664
782
  const { colors: colors2, fonts, components } = useTheme();
665
- const [searchQuery, setSearchQuery] = useState3("");
783
+ const [searchQuery, setSearchQuery] = useState4("");
666
784
  const preferredCurrencies = preferredCurrencyCodes.map(
667
785
  (code) => currencies.find(
668
786
  (currency) => currency.currency_code.toLowerCase() === code.toLowerCase()
@@ -823,8 +941,8 @@ var en_default = {
823
941
  seeTerms: "See terms",
824
942
  termsApply: "Terms apply"
825
943
  },
826
- supportedToken: "Supported token",
827
- supportedChain: "Supported chain",
944
+ selectedToken: "Selected token",
945
+ selectedChain: "Selected chain",
828
946
  depositAddress: {
829
947
  label: "Your deposit address",
830
948
  tooltip: "Send any supported token to this address, and it will be automatically converted to {{token}} in your account."
@@ -838,7 +956,19 @@ var en_default = {
838
956
  minimumDeposit: "Minimum: {{amount}}",
839
957
  minimumDepositTooltip: "The minimum amount you can deposit on the selected network.",
840
958
  selectTokenDeposit: "Your deposit token",
841
- selectTokenDepositTooltip: "Select the token you want to deposit with in order to begin the deposit process."
959
+ selectTokenDepositTooltip: "Select the token you want to deposit with in order to begin the deposit process.",
960
+ addressValidation: {
961
+ validating: "Verifying recipient address...",
962
+ unableToReceiveFunds: "Unable to Receive Funds",
963
+ errors: {
964
+ token_not_supported: "The destination token is not supported",
965
+ not_opted_in: "Please make sure you opt-in {{token_symbol}}({{chain_name}}) before receiving funds",
966
+ insufficient_balance: "Recipient account does not meet the minimum balance requirement",
967
+ account_not_found: "Recipient account does not exist on {{chain_name}}",
968
+ validation_error: "Unable to verify recipient address on {{chain_name}}"
969
+ },
970
+ defaultError: "The recipient address cannot receive funds for the selected token"
971
+ }
842
972
  },
843
973
  depositModal: {
844
974
  transferCrypto: {
@@ -847,7 +977,7 @@ var en_default = {
847
977
  },
848
978
  depositWithCard: {
849
979
  title: "Deposit with Card",
850
- subtitle: "$50,000 limit \u2022 2 min"
980
+ subtitle: "$50,000 limit"
851
981
  },
852
982
  quotes: "Quotes"
853
983
  },
@@ -865,9 +995,16 @@ var en_default = {
865
995
 
866
996
  // src/lib/i18n.ts
867
997
  var i18n = en_default;
998
+ function interpolate(template, params) {
999
+ if (!params) return template;
1000
+ return template.replace(/\{\{(\w+)\}\}/g, (_, key) => {
1001
+ const value = params[key];
1002
+ return value !== void 0 ? String(value) : `{{${key}}}`;
1003
+ });
1004
+ }
868
1005
 
869
1006
  // src/hooks/use-deposit-polling.ts
870
- import { useState as useState4, useEffect as useEffect2, useRef } from "react";
1007
+ import { useState as useState5, useEffect as useEffect3, useRef } from "react";
871
1008
  import {
872
1009
  queryExecutions,
873
1010
  ExecutionStatus
@@ -879,14 +1016,14 @@ function useDepositPolling({
879
1016
  onDepositSuccess,
880
1017
  onDepositError
881
1018
  }) {
882
- const [executions, setExecutions] = useState4([]);
883
- const [isPolling, setIsPolling] = useState4(false);
1019
+ const [executions, setExecutions] = useState5([]);
1020
+ const [isPolling, setIsPolling] = useState5(false);
884
1021
  const pollingIntervalRef = useRef(
885
1022
  null
886
1023
  );
887
- const [modalOpenedAt] = useState4(/* @__PURE__ */ new Date());
888
- const [trackedExecutions, setTrackedExecutions] = useState4(/* @__PURE__ */ new Map());
889
- useEffect2(() => {
1024
+ const [modalOpenedAt] = useState5(/* @__PURE__ */ new Date());
1025
+ const [trackedExecutions, setTrackedExecutions] = useState5(/* @__PURE__ */ new Map());
1026
+ useEffect3(() => {
890
1027
  if (!userId || !modalOpenedAt || !enabled) return;
891
1028
  const pollInterval = setInterval(async () => {
892
1029
  try {
@@ -989,10 +1126,10 @@ function useDepositPolling({
989
1126
  }
990
1127
 
991
1128
  // src/components/deposits/DepositPollingToasts.tsx
992
- import { useState as useState7 } from "react";
1129
+ import { useState as useState8 } from "react";
993
1130
 
994
1131
  // src/components/deposits/DepositSuccessToast.tsx
995
- import { useState as useState6 } from "react";
1132
+ import { useState as useState7 } from "react";
996
1133
  import { X as X3 } from "lucide-react";
997
1134
  import {
998
1135
  ExecutionStatus as ExecutionStatus3,
@@ -1000,7 +1137,7 @@ import {
1000
1137
  } from "@unifold/core";
1001
1138
 
1002
1139
  // src/components/deposits/DepositDetailContent.tsx
1003
- import { useEffect as useEffect3, useState as useState5 } from "react";
1140
+ import { useEffect as useEffect4, useState as useState6 } from "react";
1004
1141
  import { ExternalLink, ChevronDown, ChevronUp } from "lucide-react";
1005
1142
  import {
1006
1143
  ExecutionStatus as ExecutionStatus2,
@@ -1019,12 +1156,12 @@ function formatCurrency(currency) {
1019
1156
  }
1020
1157
  function DepositDetailContent({ execution }) {
1021
1158
  const { colors: colors2, fonts, components } = useTheme();
1022
- const [chains, setChains] = useState5([]);
1023
- const [showNetworkDetails, setShowNetworkDetails] = useState5(false);
1024
- useEffect3(() => {
1159
+ const [chains, setChains] = useState6([]);
1160
+ const [showNetworkDetails, setShowNetworkDetails] = useState6(false);
1161
+ useEffect4(() => {
1025
1162
  getTokenChains().then((response) => setChains(response.data)).catch((err) => console.error("Failed to fetch chains:", err));
1026
1163
  }, []);
1027
- useEffect3(() => {
1164
+ useEffect4(() => {
1028
1165
  setShowNetworkDetails(false);
1029
1166
  }, [execution?.id]);
1030
1167
  const isPending = execution.status === ExecutionStatus2.PENDING || execution.status === ExecutionStatus2.WAITING || execution.status === ExecutionStatus2.DELAYED;
@@ -1319,6 +1456,37 @@ function DepositDetailContent({ execution }) {
1319
1456
  ]
1320
1457
  }
1321
1458
  ),
1459
+ isPending && /* @__PURE__ */ jsxs6(
1460
+ "div",
1461
+ {
1462
+ className: "uf-flex uf-justify-between uf-items-center uf-px-4 uf-py-3 uf-border-b",
1463
+ style: { borderColor: colors2.border },
1464
+ children: [
1465
+ /* @__PURE__ */ jsx7(
1466
+ "span",
1467
+ {
1468
+ className: "uf-text-sm",
1469
+ style: {
1470
+ color: components.card.labelColor,
1471
+ fontFamily: fonts.regular
1472
+ },
1473
+ children: "Estimated delivery time"
1474
+ }
1475
+ ),
1476
+ /* @__PURE__ */ jsx7(
1477
+ "span",
1478
+ {
1479
+ style: {
1480
+ color: components.card.titleColor,
1481
+ fontFamily: fonts.regular,
1482
+ fontSize: "14px"
1483
+ },
1484
+ children: formatEstimatedTime(execution?.estimated_processing_time)
1485
+ }
1486
+ )
1487
+ ]
1488
+ }
1489
+ ),
1322
1490
  /* @__PURE__ */ jsxs6(
1323
1491
  "div",
1324
1492
  {
@@ -1505,7 +1673,7 @@ function DepositSuccessToast({
1505
1673
  onClose,
1506
1674
  execution
1507
1675
  }) {
1508
- const [detailModalOpen, setDetailModalOpen] = useState6(false);
1676
+ const [detailModalOpen, setDetailModalOpen] = useState7(false);
1509
1677
  const { themeClass, colors: colors2, fonts, components } = useTheme();
1510
1678
  const isPending = status === ExecutionStatus3.PENDING || status === ExecutionStatus3.WAITING || status === ExecutionStatus3.DELAYED;
1511
1679
  const formatDateTime = (timestamp) => {
@@ -1639,14 +1807,28 @@ function DepositSuccessToast({
1639
1807
  }
1640
1808
  )
1641
1809
  ] }),
1642
- /* @__PURE__ */ jsx8(
1643
- "div",
1644
- {
1645
- className: "uf-font-medium uf-text-sm uf-flex-shrink-0",
1646
- style: { color: colors2.background },
1647
- children: formatUsdAmount(sourceAmountUsd)
1648
- }
1649
- ),
1810
+ /* @__PURE__ */ jsxs7("div", { className: "uf-flex-shrink-0 uf-text-right", children: [
1811
+ /* @__PURE__ */ jsx8(
1812
+ "div",
1813
+ {
1814
+ className: "uf-font-medium uf-text-sm",
1815
+ style: { color: colors2.background },
1816
+ children: formatUsdAmount(sourceAmountUsd)
1817
+ }
1818
+ ),
1819
+ isPending && execution?.estimated_processing_time && /* @__PURE__ */ jsxs7(
1820
+ "p",
1821
+ {
1822
+ className: "uf-text-xs",
1823
+ style: { color: colors2.foregroundMuted },
1824
+ children: [
1825
+ "Est.",
1826
+ " ",
1827
+ formatEstimatedTime(execution.estimated_processing_time)
1828
+ ]
1829
+ }
1830
+ )
1831
+ ] }),
1650
1832
  /* @__PURE__ */ jsx8(
1651
1833
  "button",
1652
1834
  {
@@ -1695,7 +1877,7 @@ function DepositPollingToasts({
1695
1877
  executions,
1696
1878
  horizontalPadding = "24px"
1697
1879
  }) {
1698
- const [closedExecutionIds, setClosedExecutionIds] = useState7(
1880
+ const [closedExecutionIds, setClosedExecutionIds] = useState8(
1699
1881
  /* @__PURE__ */ new Set()
1700
1882
  );
1701
1883
  const handleClose = (executionId) => {
@@ -1776,28 +1958,28 @@ function BuyWithCard({
1776
1958
  assetCdnUrl
1777
1959
  }) {
1778
1960
  const { colors: colors2, fonts, components } = useTheme();
1779
- const [amount, setAmount] = useState8("");
1780
- const [currency, setCurrency] = useState8("usd");
1781
- const [hasManualCurrencySelection, setHasManualCurrencySelection] = useState8(false);
1782
- const [hasManualAmountEntry, setHasManualAmountEntry] = useState8(false);
1783
- const [showCurrencyModal, setShowCurrencyModal] = useState8(false);
1784
- const [quotes, setQuotes] = useState8([]);
1785
- const [quotesLoading, setQuotesLoading] = useState8(false);
1786
- const [quotesError, setQuotesError] = useState8(null);
1787
- const [amountValidationError, setAmountValidationError] = useState8(null);
1788
- const [internalView, setInternalView] = useState8("amount");
1789
- const [defaultToken, setDefaultToken] = useState8(
1961
+ const [amount, setAmount] = useState9("");
1962
+ const [currency, setCurrency] = useState9("usd");
1963
+ const [hasManualCurrencySelection, setHasManualCurrencySelection] = useState9(false);
1964
+ const [hasManualAmountEntry, setHasManualAmountEntry] = useState9(false);
1965
+ const [showCurrencyModal, setShowCurrencyModal] = useState9(false);
1966
+ const [quotes, setQuotes] = useState9([]);
1967
+ const [quotesLoading, setQuotesLoading] = useState9(false);
1968
+ const [quotesError, setQuotesError] = useState9(null);
1969
+ const [amountValidationError, setAmountValidationError] = useState9(null);
1970
+ const [internalView, setInternalView] = useState9("amount");
1971
+ const [defaultToken, setDefaultToken] = useState9(
1790
1972
  null
1791
1973
  );
1792
- const [defaultTokenLoading, setDefaultTokenLoading] = useState8(false);
1974
+ const [defaultTokenLoading, setDefaultTokenLoading] = useState9(false);
1793
1975
  const { userIpInfo, isLoading: isLoadingIp } = useUserIp();
1794
- const [onrampSession, setOnrampSession] = useState8(
1976
+ const [onrampSession, setOnrampSession] = useState9(
1795
1977
  null
1796
1978
  );
1797
1979
  const currentView = externalView ?? internalView;
1798
1980
  const showQuotesView = currentView === "quotes";
1799
1981
  const showOnrampView = currentView === "onramp";
1800
- useEffect4(() => {
1982
+ useEffect5(() => {
1801
1983
  if (externalView) {
1802
1984
  setInternalView(externalView);
1803
1985
  }
@@ -1810,31 +1992,31 @@ function BuyWithCard({
1810
1992
  onViewChange?.(newView);
1811
1993
  }
1812
1994
  };
1813
- const [selectedProvider, setSelectedProvider] = useState8(
1995
+ const [selectedProvider, setSelectedProvider] = useState9(
1814
1996
  null
1815
1997
  );
1816
- const [isAutoSelected, setIsAutoSelected] = useState8(true);
1817
- const [autoSelectedProvider, setAutoSelectedProvider] = useState8(null);
1818
- const [hoveredProviderIndex, setHoveredProviderIndex] = useState8(null);
1819
- const [hasManualSelection, setHasManualSelection] = useState8(false);
1998
+ const [isAutoSelected, setIsAutoSelected] = useState9(true);
1999
+ const [autoSelectedProvider, setAutoSelectedProvider] = useState9(null);
2000
+ const [hoveredProviderIndex, setHoveredProviderIndex] = useState9(null);
2001
+ const [hasManualSelection, setHasManualSelection] = useState9(false);
1820
2002
  const selectedProviderRef = useRef2(null);
1821
2003
  const hasManualSelectionRef = useRef2(false);
1822
- useEffect4(() => {
2004
+ useEffect5(() => {
1823
2005
  selectedProviderRef.current = selectedProvider;
1824
2006
  }, [selectedProvider]);
1825
- useEffect4(() => {
2007
+ useEffect5(() => {
1826
2008
  hasManualSelectionRef.current = hasManualSelection;
1827
2009
  }, [hasManualSelection]);
1828
- const [internalWallets, setInternalWallets] = useState8([]);
1829
- const [walletsLoading, setWalletsLoading] = useState8(
2010
+ const [internalWallets, setInternalWallets] = useState9([]);
2011
+ const [walletsLoading, setWalletsLoading] = useState9(
1830
2012
  !externalWallets?.length
1831
2013
  );
1832
2014
  const wallets = externalWallets?.length ? externalWallets : internalWallets;
1833
- const [countdown, setCountdown] = useState8(60);
1834
- const [fiatCurrencies, setFiatCurrencies] = useState8([]);
1835
- const [preferredCurrencyCodes, setPreferredCurrencyCodes] = useState8([]);
1836
- const [currenciesLoading, setCurrenciesLoading] = useState8(true);
1837
- const [destinationToken, setDestinationToken] = useState8(null);
2015
+ const [countdown, setCountdown] = useState9(60);
2016
+ const [fiatCurrencies, setFiatCurrencies] = useState9([]);
2017
+ const [preferredCurrencyCodes, setPreferredCurrencyCodes] = useState9([]);
2018
+ const [currenciesLoading, setCurrenciesLoading] = useState9(true);
2019
+ const [destinationToken, setDestinationToken] = useState9(null);
1838
2020
  const { executions, isPolling } = useDepositPolling({
1839
2021
  userId,
1840
2022
  publishableKey,
@@ -1846,7 +2028,7 @@ function BuyWithCard({
1846
2028
  const destinationTokenIcon = destinationToken?.icon_url;
1847
2029
  const destinationChainIcon = destinationToken?.chain_icon_url;
1848
2030
  const destinationChainName = destinationToken?.chain_name;
1849
- useEffect4(() => {
2031
+ useEffect5(() => {
1850
2032
  async function fetchFiatCurrencies() {
1851
2033
  try {
1852
2034
  const response = await getFiatCurrencies(publishableKey);
@@ -1860,7 +2042,7 @@ function BuyWithCard({
1860
2042
  }
1861
2043
  fetchFiatCurrencies();
1862
2044
  }, [publishableKey]);
1863
- useEffect4(() => {
2045
+ useEffect5(() => {
1864
2046
  if (hasManualCurrencySelection) return;
1865
2047
  if (fiatCurrencies.length === 0 || !userIpInfo?.alpha2) return;
1866
2048
  const userCountryCode = userIpInfo.alpha2.toUpperCase();
@@ -1888,7 +2070,7 @@ function BuyWithCard({
1888
2070
  hasManualAmountEntry
1889
2071
  ]);
1890
2072
  const prevCurrencyRef = useRef2(null);
1891
- useEffect4(() => {
2073
+ useEffect5(() => {
1892
2074
  if (fiatCurrencies.length === 0) return;
1893
2075
  if (prevCurrencyRef.current !== null && prevCurrencyRef.current !== currency) {
1894
2076
  const currentCurrency = fiatCurrencies.find(
@@ -1900,7 +2082,7 @@ function BuyWithCard({
1900
2082
  }
1901
2083
  prevCurrencyRef.current = currency;
1902
2084
  }, [currency]);
1903
- useEffect4(() => {
2085
+ useEffect5(() => {
1904
2086
  if (externalWallets?.length) {
1905
2087
  setWalletsLoading(false);
1906
2088
  return;
@@ -1953,7 +2135,7 @@ function BuyWithCard({
1953
2135
  publishableKey,
1954
2136
  externalWallets
1955
2137
  ]);
1956
- useEffect4(() => {
2138
+ useEffect5(() => {
1957
2139
  async function fetchDestinationToken() {
1958
2140
  try {
1959
2141
  const response = await getTokenMetadata(
@@ -1971,7 +2153,7 @@ function BuyWithCard({
1971
2153
  }
1972
2154
  fetchDestinationToken();
1973
2155
  }, [publishableKey]);
1974
- useEffect4(() => {
2156
+ useEffect5(() => {
1975
2157
  async function fetchDefaultToken() {
1976
2158
  if (!destinationTokenAddress || !destinationChainId || !destinationChainType) {
1977
2159
  return;
@@ -2007,7 +2189,7 @@ function BuyWithCard({
2007
2189
  isLoadingIp,
2008
2190
  publishableKey
2009
2191
  ]);
2010
- useEffect4(() => {
2192
+ useEffect5(() => {
2011
2193
  const amountNum = parseFloat(amount);
2012
2194
  if (isNaN(amountNum) || amountNum <= 0) {
2013
2195
  setQuotes([]);
@@ -2116,7 +2298,7 @@ function BuyWithCard({
2116
2298
  setQuotesLoading(false);
2117
2299
  }
2118
2300
  };
2119
- useEffect4(() => {
2301
+ useEffect5(() => {
2120
2302
  if (quotes.length === 0) return;
2121
2303
  const timer = setInterval(() => {
2122
2304
  setCountdown((prev) => {
@@ -2451,6 +2633,21 @@ function BuyWithCard({
2451
2633
  },
2452
2634
  children: quotesError
2453
2635
  }
2636
+ ),
2637
+ defaultToken?.estimated_processing_time && !quotesLoading && selectedProvider && /* @__PURE__ */ jsxs8(
2638
+ "div",
2639
+ {
2640
+ className: "uf-text-xs uf-mt-2 uf-px-1",
2641
+ style: {
2642
+ color: components.card.subtitleColor,
2643
+ fontFamily: fonts.regular
2644
+ },
2645
+ children: [
2646
+ "Estimated delivery time:",
2647
+ " ",
2648
+ formatEstimatedTime(defaultToken.estimated_processing_time)
2649
+ ]
2650
+ }
2454
2651
  )
2455
2652
  ] }),
2456
2653
  /* @__PURE__ */ jsx10(
@@ -2668,7 +2865,7 @@ function BuyWithCard({
2668
2865
  }
2669
2866
 
2670
2867
  // src/components/deposits/DepositsModal.tsx
2671
- import { useEffect as useEffect5, useState as useState9 } from "react";
2868
+ import { useEffect as useEffect6, useState as useState10 } from "react";
2672
2869
 
2673
2870
  // src/components/deposits/DepositExecutionItem.tsx
2674
2871
  import { ChevronRight as ChevronRight2 } from "lucide-react";
@@ -2913,9 +3110,9 @@ function DepositsModal({
2913
3110
  themeClass = ""
2914
3111
  }) {
2915
3112
  const { colors: colors2 } = useTheme();
2916
- const [allExecutions, setAllExecutions] = useState9(sessionExecutions);
2917
- const [selectedExecution, setSelectedExecution] = useState9(null);
2918
- useEffect5(() => {
3113
+ const [allExecutions, setAllExecutions] = useState10(sessionExecutions);
3114
+ const [selectedExecution, setSelectedExecution] = useState10(null);
3115
+ useEffect6(() => {
2919
3116
  if (!open || !userId) return;
2920
3117
  const fetchExecutions = async () => {
2921
3118
  try {
@@ -2937,7 +3134,7 @@ function DepositsModal({
2937
3134
  clearInterval(pollInterval);
2938
3135
  };
2939
3136
  }, [open, userId, publishableKey, sessionExecutions]);
2940
- useEffect5(() => {
3137
+ useEffect6(() => {
2941
3138
  if (!open) {
2942
3139
  setSelectedExecution(null);
2943
3140
  }
@@ -3325,8 +3522,68 @@ function useAllowedCountry(publishableKey) {
3325
3522
  };
3326
3523
  }
3327
3524
 
3525
+ // src/hooks/use-address-validation.ts
3526
+ import { useQuery as useQuery3 } from "@tanstack/react-query";
3527
+ import {
3528
+ verifyRecipientAddress
3529
+ } from "@unifold/core";
3530
+ function useAddressValidation({
3531
+ recipientAddress,
3532
+ destinationChainType,
3533
+ destinationChainId,
3534
+ destinationTokenAddress,
3535
+ publishableKey,
3536
+ enabled = true,
3537
+ refetchOnMount = false
3538
+ }) {
3539
+ const shouldValidate = enabled && !!recipientAddress && !!destinationChainType && !!destinationChainId && !!destinationTokenAddress;
3540
+ const { data, isLoading, error } = useQuery3({
3541
+ queryKey: [
3542
+ "unifold",
3543
+ "addressValidation",
3544
+ recipientAddress,
3545
+ destinationChainType,
3546
+ destinationChainId,
3547
+ destinationTokenAddress
3548
+ ],
3549
+ queryFn: () => verifyRecipientAddress(
3550
+ {
3551
+ chain_type: destinationChainType,
3552
+ chain_id: destinationChainId,
3553
+ token_address: destinationTokenAddress,
3554
+ recipient_address: recipientAddress
3555
+ },
3556
+ publishableKey
3557
+ ),
3558
+ enabled: shouldValidate,
3559
+ refetchOnMount,
3560
+ refetchOnReconnect: false,
3561
+ refetchOnWindowFocus: false,
3562
+ staleTime: 1e3 * 60 * 5,
3563
+ // 5 minutes - address state can change
3564
+ gcTime: 1e3 * 60 * 30
3565
+ // 30 minutes
3566
+ });
3567
+ if (!shouldValidate) {
3568
+ return {
3569
+ isValid: null,
3570
+ failureCode: null,
3571
+ metadata: null,
3572
+ isLoading: false,
3573
+ error: null
3574
+ };
3575
+ }
3576
+ return {
3577
+ isValid: data?.valid ?? null,
3578
+ failureCode: data?.failure_code ?? null,
3579
+ metadata: data?.metadata ?? null,
3580
+ isLoading,
3581
+ error: error ?? null
3582
+ };
3583
+ }
3584
+
3328
3585
  // src/components/deposits/TransferCryptoSingleInput.tsx
3329
- import { useState as useState14, useEffect as useEffect11 } from "react";
3586
+ import { useState as useState15, useEffect as useEffect12 } from "react";
3330
3587
  import {
3331
3588
  ChevronDown as ChevronDown3,
3332
3589
  ChevronUp as ChevronUp2,
@@ -3342,7 +3599,7 @@ import {
3342
3599
  } from "lucide-react";
3343
3600
 
3344
3601
  // src/components/deposits/StyledQRCode.tsx
3345
- import { useEffect as useEffect9, useRef as useRef3 } from "react";
3602
+ import { useEffect as useEffect10, useRef as useRef3 } from "react";
3346
3603
  import QRCodeStyling from "qr-code-styling";
3347
3604
  import { jsx as jsx17 } from "react/jsx-runtime";
3348
3605
  function StyledQRCode({
@@ -3354,7 +3611,7 @@ function StyledQRCode({
3354
3611
  }) {
3355
3612
  const ref = useRef3(null);
3356
3613
  const qrCodeRef = useRef3(null);
3357
- useEffect9(() => {
3614
+ useEffect10(() => {
3358
3615
  if (!ref.current) return;
3359
3616
  if (!qrCodeRef.current) {
3360
3617
  qrCodeRef.current = new QRCodeStyling({
@@ -3394,7 +3651,7 @@ function StyledQRCode({
3394
3651
  qrCodeRef.current.append(ref.current);
3395
3652
  }
3396
3653
  }, []);
3397
- useEffect9(() => {
3654
+ useEffect10(() => {
3398
3655
  if (qrCodeRef.current) {
3399
3656
  qrCodeRef.current.update({
3400
3657
  data: value,
@@ -3435,7 +3692,7 @@ function StyledQRCode({
3435
3692
  }
3436
3693
 
3437
3694
  // src/components/deposits/TokenSelectorSheet.tsx
3438
- import { useState as useState13, useMemo as useMemo3, useEffect as useEffect10 } from "react";
3695
+ import { useState as useState14, useMemo as useMemo3, useEffect as useEffect11 } from "react";
3439
3696
  import { ArrowLeft as ArrowLeft2, X as X4 } from "lucide-react";
3440
3697
  import { jsx as jsx18, jsxs as jsxs14 } from "react/jsx-runtime";
3441
3698
  var STORAGE_KEY = "unifold_recent_tokens";
@@ -3493,10 +3750,10 @@ function TokenSelectorSheet({
3493
3750
  }) {
3494
3751
  const { themeClass, colors: colors2, fonts, components } = useTheme();
3495
3752
  const isDarkMode = themeClass.includes("uf-dark");
3496
- const [searchQuery, setSearchQuery] = useState13("");
3497
- const [recentTokens, setRecentTokens] = useState13([]);
3498
- const [hoveredTokenKey, setHoveredTokenKey] = useState13(null);
3499
- useEffect10(() => {
3753
+ const [searchQuery, setSearchQuery] = useState14("");
3754
+ const [recentTokens, setRecentTokens] = useState14([]);
3755
+ const [hoveredTokenKey, setHoveredTokenKey] = useState14(null);
3756
+ useEffect11(() => {
3500
3757
  setRecentTokens(getRecentTokens());
3501
3758
  }, []);
3502
3759
  const allOptions = useMemo3(() => {
@@ -3938,13 +4195,13 @@ function TransferCryptoSingleInput({
3938
4195
  }) {
3939
4196
  const { themeClass, colors: colors2, fonts, components } = useTheme();
3940
4197
  const isDarkMode = themeClass.includes("uf-dark");
3941
- const [token, setToken] = useState14("USDC");
3942
- const [chain, setChain] = useState14("solana:mainnet");
3943
- const [copied, setCopied] = useState14(false);
3944
- const [internalWallets, setInternalWallets] = useState14([]);
3945
- const [loading, setLoading] = useState14(!externalWallets?.length);
4198
+ const [token, setToken] = useState15("USDC");
4199
+ const [chain, setChain] = useState15("solana:mainnet");
4200
+ const [copied, setCopied] = useState15(false);
4201
+ const [internalWallets, setInternalWallets] = useState15([]);
4202
+ const [loading, setLoading] = useState15(!externalWallets?.length);
3946
4203
  const wallets = externalWallets?.length ? externalWallets : internalWallets;
3947
- const [error, setError] = useState14(null);
4204
+ const [error, setError] = useState15(null);
3948
4205
  const { executions: depositExecutions, isPolling } = useDepositPolling({
3949
4206
  userId,
3950
4207
  publishableKey,
@@ -3952,11 +4209,11 @@ function TransferCryptoSingleInput({
3952
4209
  onDepositSuccess,
3953
4210
  onDepositError
3954
4211
  });
3955
- const [supportedTokens, setSupportedTokens] = useState14([]);
3956
- const [tokensLoading, setTokensLoading] = useState14(true);
3957
- const [detailsExpanded, setDetailsExpanded] = useState14(false);
3958
- const [depositsModalOpen, setDepositsModalOpen] = useState14(false);
3959
- const [tokenSelectorOpen, setTokenSelectorOpen] = useState14(false);
4212
+ const [supportedTokens, setSupportedTokens] = useState15([]);
4213
+ const [tokensLoading, setTokensLoading] = useState15(true);
4214
+ const [detailsExpanded, setDetailsExpanded] = useState15(false);
4215
+ const [depositsModalOpen, setDepositsModalOpen] = useState15(false);
4216
+ const [tokenSelectorOpen, setTokenSelectorOpen] = useState15(false);
3960
4217
  const allChainsMap = /* @__PURE__ */ new Map();
3961
4218
  supportedTokens.forEach((t5) => {
3962
4219
  t5.chains.forEach((c) => {
@@ -3974,7 +4231,7 @@ function TransferCryptoSingleInput({
3974
4231
  const currentChainType = currentChainData?.chain_type || "ethereum";
3975
4232
  const currentWallet = getWalletByChainType2(wallets, currentChainType);
3976
4233
  const depositAddress = currentWallet?.address || "";
3977
- useEffect11(() => {
4234
+ useEffect12(() => {
3978
4235
  async function fetchSupportedTokens() {
3979
4236
  try {
3980
4237
  setTokensLoading(true);
@@ -4046,12 +4303,12 @@ function TransferCryptoSingleInput({
4046
4303
  destinationChainId,
4047
4304
  destinationChainType
4048
4305
  ]);
4049
- useEffect11(() => {
4306
+ useEffect12(() => {
4050
4307
  if (onExecutionsChange) {
4051
4308
  onExecutionsChange(depositExecutions);
4052
4309
  }
4053
4310
  }, [depositExecutions, onExecutionsChange]);
4054
- useEffect11(() => {
4311
+ useEffect12(() => {
4055
4312
  if (externalWallets?.length) {
4056
4313
  setLoading(false);
4057
4314
  return;
@@ -4108,7 +4365,7 @@ function TransferCryptoSingleInput({
4108
4365
  publishableKey,
4109
4366
  externalWallets
4110
4367
  ]);
4111
- useEffect11(() => {
4368
+ useEffect12(() => {
4112
4369
  if (!supportedTokens.length) return;
4113
4370
  const currentToken = supportedTokens.find((t5) => t5.symbol === token);
4114
4371
  if (!currentToken || currentToken.chains.length === 0) return;
@@ -4300,24 +4557,24 @@ function TransferCryptoSingleInput({
4300
4557
  )
4301
4558
  ] })
4302
4559
  ] }),
4303
- loading ? /* @__PURE__ */ jsx20("div", { className: "uf-bg-secondary uf-rounded-lg uf-px-3 uf-py-2.5 uf-text-xs uf-text-muted-foreground uf-animate-pulse", children: t2.loading }) : error ? /* @__PURE__ */ jsx20("div", { className: "uf-bg-secondary uf-rounded-lg uf-px-3 uf-py-2.5 uf-text-xs uf-text-red-400", children: error }) : /* @__PURE__ */ jsxs15("div", { className: "uf-relative", children: [
4304
- /* @__PURE__ */ jsx20(
4305
- "button",
4306
- {
4307
- onClick: handleCopyAddress,
4308
- disabled: !depositAddress,
4309
- className: "uf-w-full uf-bg-secondary hover:uf-bg-accent uf-rounded-lg uf-px-2.5 uf-py-2.5 uf-text-xs uf-font-mono uf-break-all uf-text-left uf-transition-colors uf-cursor-pointer disabled:uf-opacity-50 disabled:uf-cursor-not-allowed",
4310
- children: depositAddress || t2.noAddressAvailable
4311
- }
4312
- ),
4313
- depositAddress && /* @__PURE__ */ jsx20(
4314
- "span",
4315
- {
4316
- className: `uf-absolute uf-inset-y-0 uf-right-3 uf-flex uf-items-center uf-pointer-events-none uf-transition-colors ${copied ? "uf-text-green-500" : "uf-text-muted-foreground"}`,
4317
- children: copied ? /* @__PURE__ */ jsx20(Check3, { className: "uf-w-3 uf-h-3" }) : /* @__PURE__ */ jsx20(Copy, { className: "uf-w-3 uf-h-3" })
4318
- }
4319
- )
4320
- ] })
4560
+ loading ? /* @__PURE__ */ jsx20("div", { className: "uf-bg-secondary uf-rounded-lg uf-px-3 uf-py-2.5 uf-text-xs uf-text-muted-foreground uf-animate-pulse", children: t2.loading }) : error ? /* @__PURE__ */ jsx20("div", { className: "uf-bg-secondary uf-rounded-lg uf-px-3 uf-py-2.5 uf-text-xs uf-text-red-400", children: error }) : /* @__PURE__ */ jsxs15(
4561
+ "button",
4562
+ {
4563
+ onClick: handleCopyAddress,
4564
+ disabled: !depositAddress,
4565
+ className: "uf-w-full uf-bg-secondary hover:uf-bg-accent uf-rounded-lg uf-px-3 uf-py-2.5 uf-flex uf-items-center uf-justify-between uf-gap-2 uf-transition-colors uf-cursor-pointer disabled:uf-opacity-50 disabled:uf-cursor-not-allowed",
4566
+ children: [
4567
+ /* @__PURE__ */ jsx20("span", { className: "uf-text-xs uf-font-mono uf-truncate uf-min-w-0", children: depositAddress ? truncateAddress(depositAddress, 18, 12) : t2.noAddressAvailable }),
4568
+ depositAddress && /* @__PURE__ */ jsx20(
4569
+ "span",
4570
+ {
4571
+ className: `uf-flex-shrink-0 uf-transition-colors ${copied ? "uf-text-green-500" : "uf-text-muted-foreground"}`,
4572
+ children: copied ? /* @__PURE__ */ jsx20(Check3, { className: "uf-w-3.5 uf-h-3.5" }) : /* @__PURE__ */ jsx20(Copy, { className: "uf-w-3.5 uf-h-3.5" })
4573
+ }
4574
+ )
4575
+ ]
4576
+ }
4577
+ )
4321
4578
  ] }),
4322
4579
  /* @__PURE__ */ jsxs15("div", { className: "uf-bg-secondary uf-rounded-xl uf-px-2.5", children: [
4323
4580
  /* @__PURE__ */ jsxs15(
@@ -4486,7 +4743,7 @@ function TransferCryptoSingleInput({
4486
4743
  }
4487
4744
 
4488
4745
  // src/components/deposits/TransferCryptoDoubleInput.tsx
4489
- import { useState as useState15, useEffect as useEffect12 } from "react";
4746
+ import { useState as useState16, useEffect as useEffect13 } from "react";
4490
4747
  import {
4491
4748
  ChevronDown as ChevronDown5,
4492
4749
  ChevronUp as ChevronUp4,
@@ -4651,13 +4908,13 @@ function TransferCryptoDoubleInput({
4651
4908
  }) {
4652
4909
  const { themeClass, colors: colors2, fonts, components } = useTheme();
4653
4910
  const isDarkMode = themeClass.includes("uf-dark");
4654
- const [token, setToken] = useState15("USDC");
4655
- const [chain, setChain] = useState15("solana:mainnet");
4656
- const [copied, setCopied] = useState15(false);
4657
- const [internalWallets, setInternalWallets] = useState15([]);
4658
- const [loading, setLoading] = useState15(!externalWallets?.length);
4911
+ const [token, setToken] = useState16("USDC");
4912
+ const [chain, setChain] = useState16("solana:mainnet");
4913
+ const [copied, setCopied] = useState16(false);
4914
+ const [internalWallets, setInternalWallets] = useState16([]);
4915
+ const [loading, setLoading] = useState16(!externalWallets?.length);
4659
4916
  const wallets = externalWallets?.length ? externalWallets : internalWallets;
4660
- const [error, setError] = useState15(null);
4917
+ const [error, setError] = useState16(null);
4661
4918
  const { executions: depositExecutions, isPolling } = useDepositPolling({
4662
4919
  userId,
4663
4920
  publishableKey,
@@ -4665,10 +4922,10 @@ function TransferCryptoDoubleInput({
4665
4922
  onDepositSuccess,
4666
4923
  onDepositError
4667
4924
  });
4668
- const [supportedTokens, setSupportedTokens] = useState15([]);
4669
- const [tokensLoading, setTokensLoading] = useState15(true);
4670
- const [detailsExpanded, setDetailsExpanded] = useState15(false);
4671
- const [depositsModalOpen, setDepositsModalOpen] = useState15(false);
4925
+ const [supportedTokens, setSupportedTokens] = useState16([]);
4926
+ const [tokensLoading, setTokensLoading] = useState16(true);
4927
+ const [detailsExpanded, setDetailsExpanded] = useState16(false);
4928
+ const [depositsModalOpen, setDepositsModalOpen] = useState16(false);
4672
4929
  const allChainsMap = /* @__PURE__ */ new Map();
4673
4930
  supportedTokens.forEach((t5) => {
4674
4931
  t5.chains.forEach((c) => {
@@ -4686,7 +4943,7 @@ function TransferCryptoDoubleInput({
4686
4943
  const currentChainType = currentChainData?.chain_type || "ethereum";
4687
4944
  const currentWallet = getWalletByChainType3(wallets, currentChainType);
4688
4945
  const depositAddress = currentWallet?.address || "";
4689
- useEffect12(() => {
4946
+ useEffect13(() => {
4690
4947
  async function fetchSupportedTokens() {
4691
4948
  try {
4692
4949
  setTokensLoading(true);
@@ -4729,12 +4986,12 @@ function TransferCryptoDoubleInput({
4729
4986
  destinationChainId,
4730
4987
  destinationChainType
4731
4988
  ]);
4732
- useEffect12(() => {
4989
+ useEffect13(() => {
4733
4990
  if (onExecutionsChange) {
4734
4991
  onExecutionsChange(depositExecutions);
4735
4992
  }
4736
4993
  }, [depositExecutions, onExecutionsChange]);
4737
- useEffect12(() => {
4994
+ useEffect13(() => {
4738
4995
  if (externalWallets?.length) {
4739
4996
  setLoading(false);
4740
4997
  return;
@@ -4791,7 +5048,7 @@ function TransferCryptoDoubleInput({
4791
5048
  publishableKey,
4792
5049
  externalWallets
4793
5050
  ]);
4794
- useEffect12(() => {
5051
+ useEffect13(() => {
4795
5052
  if (!supportedTokens.length) return;
4796
5053
  const currentToken = supportedTokens.find((t5) => t5.symbol === token);
4797
5054
  if (!currentToken || currentToken.chains.length === 0) return;
@@ -4881,7 +5138,7 @@ function TransferCryptoDoubleInput({
4881
5138
  children: [
4882
5139
  /* @__PURE__ */ jsxs17("div", { className: "uf-grid uf-grid-cols-2 uf-gap-2.5", children: [
4883
5140
  /* @__PURE__ */ jsxs17("div", { children: [
4884
- /* @__PURE__ */ jsx22("div", { className: "uf-text-xs uf-text-muted-foreground uf-mb-2 uf-flex uf-items-center uf-gap-1", children: t3.supportedToken }),
5141
+ /* @__PURE__ */ jsx22("div", { className: "uf-text-xs uf-text-muted-foreground uf-mb-2 uf-flex uf-items-center uf-gap-1", children: t3.selectedToken }),
4885
5142
  /* @__PURE__ */ jsxs17(
4886
5143
  Select,
4887
5144
  {
@@ -4905,7 +5162,7 @@ function TransferCryptoDoubleInput({
4905
5162
  ] }),
4906
5163
  /* @__PURE__ */ jsxs17("div", { children: [
4907
5164
  /* @__PURE__ */ jsxs17("div", { className: "uf-text-xs uf-text-muted-foreground uf-mb-2 uf-flex uf-items-center uf-gap-1", children: [
4908
- t3.supportedChain,
5165
+ t3.selectedChain,
4909
5166
  /* @__PURE__ */ jsxs17("span", { className: "uf-text-amber-400 uf-font-medium", children: [
4910
5167
  "$",
4911
5168
  minDepositUsd,
@@ -5019,24 +5276,24 @@ function TransferCryptoDoubleInput({
5019
5276
  )
5020
5277
  ] })
5021
5278
  ] }),
5022
- loading ? /* @__PURE__ */ jsx22("div", { className: "uf-bg-secondary uf-rounded-lg uf-px-3 uf-py-2.5 uf-text-xs uf-text-muted-foreground uf-animate-pulse", children: t3.loading }) : error ? /* @__PURE__ */ jsx22("div", { className: "uf-bg-secondary uf-rounded-lg uf-px-3 uf-py-2.5 uf-text-xs uf-text-red-400", children: error }) : /* @__PURE__ */ jsxs17("div", { className: "uf-relative", children: [
5023
- /* @__PURE__ */ jsx22(
5024
- "button",
5025
- {
5026
- onClick: handleCopyAddress,
5027
- disabled: !depositAddress,
5028
- className: "uf-w-full uf-bg-secondary hover:uf-bg-accent uf-rounded-lg uf-px-2.5 uf-py-2.5 uf-text-xs uf-font-mono uf-break-all uf-text-left uf-transition-colors uf-cursor-pointer disabled:uf-opacity-50 disabled:uf-cursor-not-allowed",
5029
- children: depositAddress || t3.noAddressAvailable
5030
- }
5031
- ),
5032
- depositAddress && /* @__PURE__ */ jsx22(
5033
- "span",
5034
- {
5035
- className: `uf-absolute uf-inset-y-0 uf-right-3 uf-flex uf-items-center uf-pointer-events-none uf-transition-colors ${copied ? "uf-text-green-500" : "uf-text-muted-foreground"}`,
5036
- children: copied ? /* @__PURE__ */ jsx22(Check5, { className: "uf-w-3 uf-h-3" }) : /* @__PURE__ */ jsx22(Copy2, { className: "uf-w-3 uf-h-3" })
5037
- }
5038
- )
5039
- ] })
5279
+ loading ? /* @__PURE__ */ jsx22("div", { className: "uf-bg-secondary uf-rounded-lg uf-px-3 uf-py-2.5 uf-text-xs uf-text-muted-foreground uf-animate-pulse", children: t3.loading }) : error ? /* @__PURE__ */ jsx22("div", { className: "uf-bg-secondary uf-rounded-lg uf-px-3 uf-py-2.5 uf-text-xs uf-text-red-400", children: error }) : /* @__PURE__ */ jsxs17(
5280
+ "button",
5281
+ {
5282
+ onClick: handleCopyAddress,
5283
+ disabled: !depositAddress,
5284
+ className: "uf-w-full uf-bg-secondary hover:uf-bg-accent uf-rounded-lg uf-px-3 uf-py-2.5 uf-flex uf-items-center uf-justify-between uf-gap-2 uf-transition-colors uf-cursor-pointer disabled:uf-opacity-50 disabled:uf-cursor-not-allowed",
5285
+ children: [
5286
+ /* @__PURE__ */ jsx22("span", { className: "uf-text-xs uf-font-mono uf-truncate uf-min-w-0", children: depositAddress ? truncateAddress(depositAddress, 18, 12) : t3.noAddressAvailable }),
5287
+ depositAddress && /* @__PURE__ */ jsx22(
5288
+ "span",
5289
+ {
5290
+ className: `uf-flex-shrink-0 uf-transition-colors ${copied ? "uf-text-green-500" : "uf-text-muted-foreground"}`,
5291
+ children: copied ? /* @__PURE__ */ jsx22(Check5, { className: "uf-w-3.5 uf-h-3.5" }) : /* @__PURE__ */ jsx22(Copy2, { className: "uf-w-3.5 uf-h-3.5" })
5292
+ }
5293
+ )
5294
+ ]
5295
+ }
5296
+ )
5040
5297
  ] }),
5041
5298
  /* @__PURE__ */ jsxs17("div", { className: "uf-bg-secondary uf-rounded-xl uf-px-2.5", children: [
5042
5299
  /* @__PURE__ */ jsxs17(
@@ -5242,26 +5499,27 @@ function DepositModal({
5242
5499
  destinationChainId,
5243
5500
  destinationTokenAddress,
5244
5501
  hideDepositTracker = false,
5502
+ showBalanceHeader = false,
5245
5503
  transferInputVariant = "double_input",
5246
5504
  onDepositSuccess,
5247
5505
  onDepositError,
5248
5506
  theme = "dark"
5249
5507
  }) {
5250
- const { colors: colors2 } = useTheme();
5251
- const [view, setView] = useState16("main");
5252
- const [cardView, setCardView] = useState16(
5508
+ const { colors: colors2, fonts } = useTheme();
5509
+ const [view, setView] = useState17("main");
5510
+ const [cardView, setCardView] = useState17(
5253
5511
  "amount"
5254
5512
  );
5255
- const [quotesCount, setQuotesCount] = useState16(0);
5256
- const [depositsModalOpen, setDepositsModalOpen] = useState16(false);
5257
- const [depositExecutions, setDepositExecutions] = useState16([]);
5258
- const [projectConfig, setProjectConfig] = useState16(null);
5259
- const [wallets, setWallets] = useState16([]);
5260
- const [walletsLoading, setWalletsLoading] = useState16(false);
5261
- useEffect13(() => {
5513
+ const [quotesCount, setQuotesCount] = useState17(0);
5514
+ const [depositsModalOpen, setDepositsModalOpen] = useState17(false);
5515
+ const [depositExecutions, setDepositExecutions] = useState17([]);
5516
+ const [projectConfig, setProjectConfig] = useState17(null);
5517
+ const [wallets, setWallets] = useState17([]);
5518
+ const [walletsLoading, setWalletsLoading] = useState17(false);
5519
+ useEffect14(() => {
5262
5520
  setProjectConfig(null);
5263
5521
  }, [publishableKey]);
5264
- useEffect13(() => {
5522
+ useEffect14(() => {
5265
5523
  setWallets([]);
5266
5524
  }, [
5267
5525
  userId,
@@ -5271,10 +5529,10 @@ function DepositModal({
5271
5529
  destinationTokenAddress,
5272
5530
  publishableKey
5273
5531
  ]);
5274
- const [resolvedTheme, setResolvedTheme] = useState16(
5532
+ const [resolvedTheme, setResolvedTheme] = useState17(
5275
5533
  theme === "auto" ? "dark" : theme
5276
5534
  );
5277
- useEffect13(() => {
5535
+ useEffect14(() => {
5278
5536
  if (theme === "auto") {
5279
5537
  const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
5280
5538
  setResolvedTheme(mediaQuery.matches ? "dark" : "light");
@@ -5287,7 +5545,7 @@ function DepositModal({
5287
5545
  setResolvedTheme(theme);
5288
5546
  }
5289
5547
  }, [theme]);
5290
- useEffect13(() => {
5548
+ useEffect14(() => {
5291
5549
  if (open && !projectConfig) {
5292
5550
  getProjectConfig2(publishableKey).then(setProjectConfig).catch(console.error);
5293
5551
  }
@@ -5297,7 +5555,29 @@ function DepositModal({
5297
5555
  isLoading: isCountryLoading,
5298
5556
  error: countryError
5299
5557
  } = useAllowedCountry(publishableKey);
5300
- useEffect13(() => {
5558
+ const {
5559
+ isValid: isAddressValid,
5560
+ failureCode: addressFailureCode,
5561
+ metadata: addressFailureMetadata,
5562
+ isLoading: isAddressValidationLoading
5563
+ } = useAddressValidation({
5564
+ recipientAddress,
5565
+ destinationChainType,
5566
+ destinationChainId,
5567
+ destinationTokenAddress,
5568
+ publishableKey,
5569
+ enabled: open,
5570
+ // Only validate when modal is open
5571
+ refetchOnMount: "always"
5572
+ });
5573
+ const addressValidationMessages = i18n.transferCrypto.addressValidation;
5574
+ const getAddressValidationErrorMessage = (code, metadata) => {
5575
+ if (!code) return addressValidationMessages.defaultError;
5576
+ const errors = addressValidationMessages.errors;
5577
+ const template = errors[code] ?? addressValidationMessages.defaultError;
5578
+ return interpolate(template, metadata);
5579
+ };
5580
+ useEffect14(() => {
5301
5581
  if (!open || wallets.length > 0) return;
5302
5582
  let retryTimeout = null;
5303
5583
  let isCancelled = false;
@@ -5381,10 +5661,16 @@ function DepositModal({
5381
5661
  DepositHeader,
5382
5662
  {
5383
5663
  title: modalTitle || "Deposit",
5384
- onClose: handleClose
5664
+ onClose: handleClose,
5665
+ showBalance: showBalanceHeader,
5666
+ balanceAddress: recipientAddress,
5667
+ balanceChainType: destinationChainType === "ethereum" || destinationChainType === "solana" || destinationChainType === "bitcoin" ? destinationChainType : void 0,
5668
+ balanceChainId: destinationChainId,
5669
+ balanceTokenAddress: destinationTokenAddress,
5670
+ publishableKey
5385
5671
  }
5386
5672
  ),
5387
- /* @__PURE__ */ jsx23("div", { className: "uf-pb-4 uf-space-y-3", children: isCountryLoading || !projectConfig ? /* @__PURE__ */ jsxs18(Fragment5, { children: [
5673
+ /* @__PURE__ */ jsx23("div", { className: "uf-pb-4 uf-space-y-3", children: isCountryLoading || isAddressValidationLoading || !projectConfig ? /* @__PURE__ */ jsxs18(Fragment5, { children: [
5388
5674
  /* @__PURE__ */ jsx23(SkeletonButton, { variant: "with-icons" }),
5389
5675
  /* @__PURE__ */ jsx23(SkeletonButton, { variant: "with-icons" }),
5390
5676
  !hideDepositTracker && /* @__PURE__ */ jsx23(SkeletonButton, {})
@@ -5402,6 +5688,16 @@ function DepositModal({
5402
5688
  /* @__PURE__ */ jsx23("h3", { className: "uf-text-lg uf-font-semibold uf-text-foreground uf-mb-2", children: "No Tokens Available" }),
5403
5689
  /* @__PURE__ */ jsx23("p", { className: "uf-text-sm uf-text-muted-foreground uf-max-w-[280px]", children: "There are no supported tokens available from your current location." })
5404
5690
  ] })
5691
+ ) : isAddressValid === false ? (
5692
+ /* Invalid recipient address state (e.g., Algorand not opted in) */
5693
+ /* @__PURE__ */ jsxs18("div", { className: "uf-flex uf-flex-col uf-items-center uf-justify-center uf-py-8 uf-px-4 uf-text-center", children: [
5694
+ /* @__PURE__ */ jsx23("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__ */ jsx23(AlertTriangle, { className: "uf-w-8 uf-h-8 uf-text-muted-foreground" }) }),
5695
+ /* @__PURE__ */ jsx23("h3", { className: "uf-text-lg uf-font-semibold uf-text-foreground uf-mb-2", children: addressValidationMessages.unableToReceiveFunds }),
5696
+ /* @__PURE__ */ jsx23("p", { className: "uf-text-sm uf-text-muted-foreground uf-max-w-[280px]", children: getAddressValidationErrorMessage(
5697
+ addressFailureCode,
5698
+ addressFailureMetadata
5699
+ ) })
5700
+ ] })
5405
5701
  ) : (
5406
5702
  /* Normal deposit options */
5407
5703
  /* @__PURE__ */ jsxs18(Fragment5, { children: [
@@ -5631,6 +5927,7 @@ export {
5631
5927
  getColors,
5632
5928
  mergeColors,
5633
5929
  resolveComponentTokens,
5930
+ truncateAddress,
5634
5931
  useAllowedCountry,
5635
5932
  useTheme
5636
5933
  };