@unifold/ui-web 0.1.56 → 0.1.57

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -43727,6 +43727,20 @@ async function refreshIntegrationToken(accessToken, publishableKey) {
43727
43727
  }
43728
43728
  return response.json();
43729
43729
  }
43730
+ async function revokeIntegrationToken(accessToken, publishableKey) {
43731
+ const pk = publishableKey || DEFAULT_PUBLISHABLE_KEY;
43732
+ validatePublishableKey(pk);
43733
+ await fetch(`${API_BASE_URL}/v1/public/integrations/token/revoke`, {
43734
+ method: "POST",
43735
+ headers: {
43736
+ accept: "application/json",
43737
+ "x-publishable-key": pk,
43738
+ "Content-Type": "application/json"
43739
+ },
43740
+ body: JSON.stringify({ access_token: accessToken })
43741
+ }).catch(() => {
43742
+ });
43743
+ }
43730
43744
  async function getIntegrationHoldings(provider, accessToken, publishableKey) {
43731
43745
  const pk = publishableKey || DEFAULT_PUBLISHABLE_KEY;
43732
43746
  validatePublishableKey(pk);
@@ -49400,7 +49414,8 @@ var en_default2 = {
49400
49414
  title: "Transfer Failed",
49401
49415
  tryAgain: "Try Again"
49402
49416
  },
49403
- continue: "Continue"
49417
+ continue: "Continue",
49418
+ disconnect: "Disconnect"
49404
49419
  },
49405
49420
  buyWithCard: {
49406
49421
  onramp: {
@@ -52647,6 +52662,7 @@ function PayWithExchangeButton({
52647
52662
  }
52648
52663
  function ConnectExchangeButton({
52649
52664
  onClick,
52665
+ onDisconnect,
52650
52666
  title,
52651
52667
  subtitle,
52652
52668
  exchanges,
@@ -52659,6 +52675,109 @@ function ConnectExchangeButton({
52659
52675
  setIsTouchDevice("ontouchstart" in window || navigator.maxTouchPoints > 0);
52660
52676
  }, []);
52661
52677
  const isConnected = connectedExchange != null;
52678
+ const handleDisconnectClick = (e) => {
52679
+ e.preventDefault();
52680
+ e.stopPropagation();
52681
+ onDisconnect?.();
52682
+ };
52683
+ const rowSurfaceStyle = {
52684
+ backgroundColor: isHovered ? colors2.cardHover : components.card.backgroundColor,
52685
+ borderRadius: components.card.borderRadius,
52686
+ border: `${components.card.borderWidth}px solid ${components.card.borderColor}`
52687
+ };
52688
+ const iconBlock = isConnected ? connectedExchange.iconUrl ? /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
52689
+ "img",
52690
+ {
52691
+ src: connectedExchange.iconUrl,
52692
+ alt: connectedExchange.name,
52693
+ width: 36,
52694
+ height: 36,
52695
+ className: "uf-rounded-lg"
52696
+ }
52697
+ ) : /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
52698
+ "div",
52699
+ {
52700
+ className: "uf-w-9 uf-h-9 uf-rounded-lg uf-flex uf-items-center uf-justify-center",
52701
+ style: { backgroundColor: colors2.card },
52702
+ children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
52703
+ "span",
52704
+ {
52705
+ className: "uf-text-xs uf-font-medium",
52706
+ style: { color: components.card.iconColor },
52707
+ children: connectedExchange.name.slice(0, 2).toUpperCase()
52708
+ }
52709
+ )
52710
+ }
52711
+ ) : /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { className: "uf-rounded-lg uf-p-2", children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
52712
+ Link2,
52713
+ {
52714
+ className: "uf-w-5 uf-h-5",
52715
+ style: { color: components.card.iconColor }
52716
+ }
52717
+ ) });
52718
+ const titleSubtitleBlock = /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: "uf-text-left", children: [
52719
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
52720
+ "div",
52721
+ {
52722
+ className: "uf-text-sm uf-font-light uf-mb-0.5",
52723
+ style: {
52724
+ color: components.card.titleColor,
52725
+ fontFamily: fonts.regular
52726
+ },
52727
+ children: isConnected ? connectedExchange.name : title
52728
+ }
52729
+ ),
52730
+ isConnected && connectedExchange.isLoading ? /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { className: "uf-h-3 uf-w-24 uf-bg-muted uf-rounded uf-animate-pulse" }) : /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
52731
+ "div",
52732
+ {
52733
+ className: "uf-text-xs uf-font-light",
52734
+ style: {
52735
+ color: components.card.subtitleColor,
52736
+ fontFamily: fonts.regular
52737
+ },
52738
+ children: isConnected ? connectedExchange.balanceUsd ? `$${connectedExchange.balanceUsd} \u2022 2 min` : "Deposit from exchange" : subtitle
52739
+ }
52740
+ )
52741
+ ] });
52742
+ if (isConnected && onDisconnect) {
52743
+ return /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(
52744
+ "div",
52745
+ {
52746
+ onMouseEnter: () => !isTouchDevice && setIsHovered(true),
52747
+ onMouseLeave: () => setIsHovered(false),
52748
+ onTouchStart: () => setIsHovered(false),
52749
+ className: "uf-w-full uf-transition-colors uf-flex uf-items-stretch uf-group",
52750
+ style: rowSurfaceStyle,
52751
+ children: [
52752
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(
52753
+ "button",
52754
+ {
52755
+ type: "button",
52756
+ onClick,
52757
+ className: "uf-min-w-0 uf-flex-1 uf-flex uf-items-center uf-gap-3 uf-p-3 uf-pr-1 uf-text-left uf-border-0 uf-bg-transparent hover:uf-bg-transparent",
52758
+ children: [
52759
+ iconBlock,
52760
+ titleSubtitleBlock
52761
+ ]
52762
+ }
52763
+ ),
52764
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { className: "uf-flex uf-items-center uf-gap-1 uf-shrink-0 uf-pr-2 uf-pl-0", children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
52765
+ "button",
52766
+ {
52767
+ type: "button",
52768
+ onClick: handleDisconnectClick,
52769
+ className: "uf-h-auto uf-min-h-8 uf-py-1.5 uf-px-2 uf-text-xs uf-font-light uf-shrink-0 uf-border-0 uf-bg-transparent uf-cursor-pointer hover:uf-opacity-80 uf-transition-opacity",
52770
+ style: {
52771
+ color: colors2.error,
52772
+ fontFamily: fonts.regular
52773
+ },
52774
+ children: i18n2.connectExchange.disconnect
52775
+ }
52776
+ ) })
52777
+ ]
52778
+ }
52779
+ );
52780
+ }
52662
52781
  return /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(
52663
52782
  "button",
52664
52783
  {
@@ -52667,67 +52786,11 @@ function ConnectExchangeButton({
52667
52786
  onMouseLeave: () => setIsHovered(false),
52668
52787
  onTouchStart: () => setIsHovered(false),
52669
52788
  className: "uf-w-full uf-transition-colors uf-p-3 uf-flex uf-items-center uf-justify-between uf-group",
52670
- style: {
52671
- backgroundColor: isHovered ? colors2.cardHover : components.card.backgroundColor,
52672
- borderRadius: components.card.borderRadius,
52673
- border: `${components.card.borderWidth}px solid ${components.card.borderColor}`
52674
- },
52789
+ style: rowSurfaceStyle,
52675
52790
  children: [
52676
52791
  /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: "uf-flex uf-items-center uf-gap-3", children: [
52677
- isConnected ? connectedExchange.iconUrl ? /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
52678
- "img",
52679
- {
52680
- src: connectedExchange.iconUrl,
52681
- alt: connectedExchange.name,
52682
- width: 36,
52683
- height: 36,
52684
- className: "uf-rounded-lg"
52685
- }
52686
- ) : /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
52687
- "div",
52688
- {
52689
- className: "uf-w-9 uf-h-9 uf-rounded-lg uf-flex uf-items-center uf-justify-center",
52690
- style: { backgroundColor: colors2.card },
52691
- children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
52692
- "span",
52693
- {
52694
- className: "uf-text-xs uf-font-medium",
52695
- style: { color: components.card.iconColor },
52696
- children: connectedExchange.name.slice(0, 2).toUpperCase()
52697
- }
52698
- )
52699
- }
52700
- ) : /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { className: "uf-rounded-lg uf-p-2", children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
52701
- Link2,
52702
- {
52703
- className: "uf-w-5 uf-h-5",
52704
- style: { color: components.card.iconColor }
52705
- }
52706
- ) }),
52707
- /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: "uf-text-left", children: [
52708
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
52709
- "div",
52710
- {
52711
- className: "uf-text-sm uf-font-light uf-mb-0.5",
52712
- style: {
52713
- color: components.card.titleColor,
52714
- fontFamily: fonts.regular
52715
- },
52716
- children: isConnected ? connectedExchange.name : title
52717
- }
52718
- ),
52719
- isConnected && connectedExchange.isLoading ? /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { className: "uf-h-3 uf-w-24 uf-bg-muted uf-rounded uf-animate-pulse" }) : /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
52720
- "div",
52721
- {
52722
- className: "uf-text-xs uf-font-light",
52723
- style: {
52724
- color: components.card.subtitleColor,
52725
- fontFamily: fonts.regular
52726
- },
52727
- children: isConnected ? connectedExchange.balanceUsd ? `$${connectedExchange.balanceUsd} \u2022 2 min` : "Deposit from exchange" : subtitle
52728
- }
52729
- )
52730
- ] })
52792
+ iconBlock,
52793
+ titleSubtitleBlock
52731
52794
  ] }),
52732
52795
  /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: "uf-flex uf-items-center uf-gap-1.5", children: [
52733
52796
  !isConnected && exchanges && exchanges.length > 0 ? exchanges.slice(0, 4).map((ex, i) => {
@@ -55532,6 +55595,65 @@ function useProjectConfig({
55532
55595
  });
55533
55596
  return { projectConfig, isLoading };
55534
55597
  }
55598
+ function useSupportedDepositTokens(publishableKey, options2) {
55599
+ const hasDestination = options2?.destination_token_address && options2?.destination_chain_id && options2?.destination_chain_type;
55600
+ const filteredOptions = {
55601
+ ...hasDestination ? {
55602
+ destination_token_address: options2.destination_token_address,
55603
+ destination_chain_id: options2.destination_chain_id,
55604
+ destination_chain_type: options2.destination_chain_type
55605
+ } : {},
55606
+ ...options2?.product_type ? { product_type: options2.product_type } : {}
55607
+ };
55608
+ const hasFilteredOptions = Object.keys(filteredOptions).length > 0;
55609
+ return useQuery({
55610
+ queryKey: [
55611
+ "unifold",
55612
+ "supportedDepositTokens",
55613
+ publishableKey,
55614
+ filteredOptions?.destination_token_address ?? null,
55615
+ filteredOptions?.destination_chain_id ?? null,
55616
+ filteredOptions?.destination_chain_type ?? null,
55617
+ filteredOptions?.product_type ?? null
55618
+ ],
55619
+ queryFn: () => getSupportedDepositTokens(
55620
+ publishableKey,
55621
+ hasFilteredOptions ? filteredOptions : void 0
55622
+ ),
55623
+ staleTime: 1e3 * 60 * 5,
55624
+ // 5 minutes — token list rarely changes
55625
+ gcTime: 1e3 * 60 * 30,
55626
+ // 30 minutes in cache
55627
+ refetchOnMount: false,
55628
+ refetchOnWindowFocus: false
55629
+ });
55630
+ }
55631
+ function useIntegrationTransferDefaultToken({
55632
+ params,
55633
+ publishableKey,
55634
+ enabled = true
55635
+ }) {
55636
+ return useQuery({
55637
+ queryKey: [
55638
+ "unifold",
55639
+ "integrationTransferDefaultToken",
55640
+ publishableKey,
55641
+ params?.integration_provider ?? null,
55642
+ params?.source_currency ?? null,
55643
+ params?.destination_token_address ?? null,
55644
+ params?.destination_chain_id ?? null,
55645
+ params?.destination_chain_type ?? null,
55646
+ params?.country_code ?? null,
55647
+ params?.subdivision_code ?? null
55648
+ ],
55649
+ queryFn: () => getIntegrationTransferDefaultToken(params, publishableKey),
55650
+ enabled: enabled && !!params,
55651
+ staleTime: 1e3 * 60 * 5,
55652
+ gcTime: 1e3 * 60 * 30,
55653
+ refetchOnMount: false,
55654
+ refetchOnWindowFocus: false
55655
+ });
55656
+ }
55535
55657
  var INTEGRATIONS_STORAGE_KEY = "unifold:integrations:connected";
55536
55658
  function getStoredIntegrations() {
55537
55659
  try {
@@ -55576,6 +55698,23 @@ function CoinbaseConnect({
55576
55698
  const { colors: colors2, fonts, components } = useTheme();
55577
55699
  const { projectConfig } = useProjectConfig({ publishableKey });
55578
55700
  const { userIpInfo } = useUserIp2();
55701
+ const { data: supportedTokensData, isLoading: supportedTokensLoading } = useSupportedDepositTokens(publishableKey, {
55702
+ destination_token_address: destinationTokenAddress,
55703
+ destination_chain_id: destinationChainId,
55704
+ destination_chain_type: destinationChainType
55705
+ });
55706
+ const supportedSymbols = (0, import_react18.useMemo)(() => {
55707
+ const set = /* @__PURE__ */ new Set();
55708
+ supportedTokensData?.data.forEach((token) => set.add(token.symbol.toLowerCase()));
55709
+ return set;
55710
+ }, [supportedTokensData]);
55711
+ const stablecoinSymbols = (0, import_react18.useMemo)(() => {
55712
+ const set = /* @__PURE__ */ new Set();
55713
+ supportedTokensData?.data.forEach((token) => {
55714
+ if (token.is_stablecoin) set.add(token.symbol.toLowerCase());
55715
+ });
55716
+ return set;
55717
+ }, [supportedTokensData]);
55579
55718
  const appName = projectConfig?.project_name ?? "Unifold";
55580
55719
  const t12 = i18n2.connectExchange;
55581
55720
  const initialView = skipToHoldings && getStoredIntegrationToken(IntegrationProvider.COINBASE) ? "holdings" : "select_exchange";
@@ -55598,7 +55737,43 @@ function CoinbaseConnect({
55598
55737
  const [confirmResult, setConfirmResult] = (0, import_react18.useState)(null);
55599
55738
  const [showTransferDetails, setShowTransferDetails] = (0, import_react18.useState)(false);
55600
55739
  const [transferDepositWalletId, setTransferDepositWalletId] = (0, import_react18.useState)(void 0);
55601
- const [minDepositUsd, setMinDepositUsd] = (0, import_react18.useState)(0);
55740
+ const exchangeSupportedCurrencies = (0, import_react18.useMemo)(() => {
55741
+ const set = /* @__PURE__ */ new Set();
55742
+ selectedExchange?.supported_currencies.forEach((c) => set.add(c.toLowerCase()));
55743
+ return set;
55744
+ }, [selectedExchange]);
55745
+ const defaultTokenParams = (0, import_react18.useMemo)(
55746
+ () => selectedAsset ? {
55747
+ integration_provider: IntegrationProvider.COINBASE,
55748
+ source_currency: selectedAsset.currency.toLowerCase(),
55749
+ destination_token_address: destinationTokenAddress,
55750
+ destination_chain_id: destinationChainId,
55751
+ destination_chain_type: destinationChainType,
55752
+ country_code: userIpInfo?.alpha2,
55753
+ subdivision_code: userIpInfo?.subdivisionCode ?? void 0
55754
+ } : null,
55755
+ [selectedAsset, destinationTokenAddress, destinationChainId, destinationChainType, userIpInfo?.alpha2, userIpInfo?.subdivisionCode]
55756
+ );
55757
+ const { data: defaultTokenData, isLoading: defaultTokenLoading } = useIntegrationTransferDefaultToken({
55758
+ params: defaultTokenParams,
55759
+ publishableKey
55760
+ });
55761
+ const sortedHoldings = (0, import_react18.useMemo)(() => {
55762
+ const supported = [];
55763
+ const unsupported = [];
55764
+ holdings.forEach((account) => {
55765
+ const currencyLower = account.currency.toLowerCase();
55766
+ const isSupported = (supportedSymbols.size === 0 || supportedSymbols.has(currencyLower)) && (exchangeSupportedCurrencies.size === 0 || exchangeSupportedCurrencies.has(currencyLower));
55767
+ if (isSupported) supported.push(account);
55768
+ else unsupported.push(account);
55769
+ });
55770
+ return [...supported, ...unsupported];
55771
+ }, [holdings, supportedSymbols, exchangeSupportedCurrencies]);
55772
+ const selectedHoldingIsSupported = (0, import_react18.useMemo)(() => {
55773
+ if (!selectedHolding) return false;
55774
+ const currencyLower = selectedHolding.currency.toLowerCase();
55775
+ return (supportedSymbols.size === 0 || supportedSymbols.has(currencyLower)) && (exchangeSupportedCurrencies.size === 0 || exchangeSupportedCurrencies.has(currencyLower));
55776
+ }, [selectedHolding, supportedSymbols, exchangeSupportedCurrencies]);
55602
55777
  const exchangeName = selectedExchange?.service_provider_display_name || "Exchange";
55603
55778
  const {
55604
55779
  executions: depositExecutions,
@@ -55714,53 +55889,17 @@ function CoinbaseConnect({
55714
55889
  if (pollRef.current) clearInterval(pollRef.current);
55715
55890
  };
55716
55891
  }, []);
55717
- (0, import_react18.useEffect)(() => {
55718
- if (view !== "enter_amount" || !selectedAsset) return;
55719
- let cancelled = false;
55720
- const fetchMinDeposit = async () => {
55721
- try {
55722
- const [defaultToken, supportedTokens] = await Promise.all([
55723
- getIntegrationTransferDefaultToken(
55724
- {
55725
- integration_provider: IntegrationProvider.COINBASE,
55726
- source_currency: selectedAsset.currency.toLowerCase(),
55727
- destination_token_address: destinationTokenAddress,
55728
- destination_chain_id: destinationChainId,
55729
- destination_chain_type: destinationChainType,
55730
- country_code: userIpInfo?.alpha2,
55731
- subdivision_code: userIpInfo?.subdivisionCode ?? void 0
55732
- },
55733
- publishableKey
55734
- ),
55735
- getSupportedDepositTokens(publishableKey, {
55736
- destination_token_address: destinationTokenAddress,
55737
- destination_chain_id: destinationChainId,
55738
- destination_chain_type: destinationChainType
55739
- })
55740
- ]);
55741
- if (cancelled) return;
55742
- const supportedToken = supportedTokens.data.find(
55743
- (t13) => t13.symbol.toLowerCase() === selectedAsset.currency.toLowerCase()
55744
- );
55745
- if (supportedToken && supportedToken.chains.length > 0) {
55746
- const matchingChain = supportedToken.chains.find(
55747
- (c) => c.chain_name.toLowerCase() === defaultToken.source_network.toLowerCase()
55748
- );
55749
- setMinDepositUsd(
55750
- matchingChain?.minimum_deposit_amount_usd ?? Math.min(...supportedToken.chains.map((c) => c.minimum_deposit_amount_usd))
55751
- );
55752
- } else {
55753
- setMinDepositUsd(0);
55754
- }
55755
- } catch {
55756
- if (!cancelled) setMinDepositUsd(0);
55757
- }
55758
- };
55759
- fetchMinDeposit();
55760
- return () => {
55761
- cancelled = true;
55762
- };
55763
- }, [view, selectedAsset, publishableKey, destinationTokenAddress, destinationChainId, destinationChainType, userIpInfo?.alpha2, userIpInfo?.subdivisionCode]);
55892
+ const minDepositUsd = (0, import_react18.useMemo)(() => {
55893
+ if (!selectedAsset || !defaultTokenData) return 0;
55894
+ const supportedToken = supportedTokensData?.data.find(
55895
+ (t13) => t13.symbol.toLowerCase() === selectedAsset.currency.toLowerCase()
55896
+ );
55897
+ if (!supportedToken || supportedToken.chains.length === 0) return 0;
55898
+ const matchingChain = supportedToken.chains.find(
55899
+ (c) => c.chain_name.toLowerCase() === defaultTokenData.source_network.toLowerCase()
55900
+ );
55901
+ return matchingChain?.minimum_deposit_amount_usd ?? Math.min(...supportedToken.chains.map((c) => c.minimum_deposit_amount_usd));
55902
+ }, [selectedAsset, supportedTokensData, defaultTokenData]);
55764
55903
  const handleSelectExchange = (exchange) => {
55765
55904
  if (!exchange.enabled) return;
55766
55905
  setSelectedExchange(exchange);
@@ -55805,7 +55944,6 @@ function CoinbaseConnect({
55805
55944
  const handleSelectAsset = (asset) => {
55806
55945
  setSelectedAsset(asset);
55807
55946
  setSendAmount("");
55808
- setMinDepositUsd(0);
55809
55947
  transitionTo("enter_amount");
55810
55948
  };
55811
55949
  const handleCreateTransfer = async () => {
@@ -55817,18 +55955,10 @@ function CoinbaseConnect({
55817
55955
  const cryptoAmount = (usdAmount / rate).toFixed(8);
55818
55956
  setIsLoading(true);
55819
55957
  try {
55820
- const { source_network, source_chain_type } = await getIntegrationTransferDefaultToken(
55821
- {
55822
- integration_provider: IntegrationProvider.COINBASE,
55823
- source_currency: selectedAsset.currency.toLowerCase(),
55824
- destination_token_address: destinationTokenAddress,
55825
- destination_chain_id: destinationChainId,
55826
- destination_chain_type: destinationChainType,
55827
- country_code: userIpInfo?.alpha2,
55828
- subdivision_code: userIpInfo?.subdivisionCode ?? void 0
55829
- },
55830
- publishableKey
55831
- );
55958
+ if (!defaultTokenData) {
55959
+ throw new Error("Transfer details not ready. Please try again.");
55960
+ }
55961
+ const { source_network, source_chain_type } = defaultTokenData;
55832
55962
  const matchingWallet = wallets.find((w) => w.chain_type === source_chain_type);
55833
55963
  const depositAddress = matchingWallet?.address ?? recipientAddress ?? "";
55834
55964
  if (!depositAddress) {
@@ -55980,13 +56110,12 @@ function CoinbaseConnect({
55980
56110
  break;
55981
56111
  }
55982
56112
  };
55983
- const STABLECOIN_SYMBOLS = /* @__PURE__ */ new Set(["USDC", "USDT", "DAI", "BUSD", "TUSD", "USDP", "GUSD", "FRAX", "LUSD", "PYUSD", "EURC", "USDS"]);
55984
56113
  const formatCryptoAmount = (amount, currency) => {
55985
56114
  const num = parseFloat(amount);
55986
56115
  if (isNaN(num)) return `${amount} ${currency.toUpperCase()}`;
55987
- const isStable = STABLECOIN_SYMBOLS.has(currency.toUpperCase());
55988
- const maxDecimals = isStable ? 2 : 8;
55989
- return `${num.toLocaleString(void 0, { minimumFractionDigits: isStable ? 2 : 0, maximumFractionDigits: maxDecimals })} ${currency.toUpperCase()}`;
56116
+ const isStable = stablecoinSymbols.has(currency.toLowerCase());
56117
+ const maxDecimals = isStable ? 2 : 6;
56118
+ return `${num.toLocaleString(void 0, { minimumFractionDigits: 2, maximumFractionDigits: maxDecimals })} ${currency.toUpperCase()}`;
55990
56119
  };
55991
56120
  const viewTransitionStyle = {
55992
56121
  opacity: isTransitioning ? 0 : 1,
@@ -56588,7 +56717,7 @@ function CoinbaseConnect({
56588
56717
  className: "uf-w-6 uf-h-6 uf-animate-spin",
56589
56718
  style: { color: colors2.foregroundMuted }
56590
56719
  }
56591
- ) }) : holdings.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("div", { className: "uf-text-center uf-py-8", children: /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
56720
+ ) }) : sortedHoldings.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("div", { className: "uf-text-center uf-py-8", children: /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
56592
56721
  "div",
56593
56722
  {
56594
56723
  className: "uf-text-sm",
@@ -56598,17 +56727,19 @@ function CoinbaseConnect({
56598
56727
  },
56599
56728
  children: "No holdings found"
56600
56729
  }
56601
- ) }) : holdings.map((account) => {
56730
+ ) }) : sortedHoldings.map((account) => {
56602
56731
  const iconUrl = account.icon_urls?.find((u) => u.format === "svg")?.url || account.icon_urls?.find((u) => u.format === "png")?.url || account.icon_url;
56603
- const isSelected = selectedHolding?.id === account.id;
56732
+ const currencyLower = account.currency.toLowerCase();
56733
+ const isSupported = (supportedSymbols.size === 0 || supportedSymbols.has(currencyLower)) && (exchangeSupportedCurrencies.size === 0 || exchangeSupportedCurrencies.has(currencyLower));
56734
+ const isSelected = isSupported && selectedHolding?.id === account.id;
56604
56735
  const usdValue = account.amount_usd ? parseFloat(account.amount_usd) : null;
56605
- const formattedUsd = usdValue != null && usdValue > 0 ? `$${usdValue.toLocaleString(void 0, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}` : null;
56736
+ const formattedUsd = usdValue != null && usdValue >= 0.01 ? `$${usdValue.toLocaleString(void 0, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}` : usdValue != null && usdValue < 0.01 && usdValue > 0 ? "< $0.01" : "$0.00";
56606
56737
  return /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)(
56607
56738
  "button",
56608
56739
  {
56609
56740
  onClick: () => setSelectedHolding(account),
56610
- disabled: isLoading,
56611
- className: "uf-w-full uf-transition-colors uf-p-3 uf-flex uf-items-center uf-justify-between",
56741
+ disabled: isLoading || !isSupported,
56742
+ className: `uf-w-full uf-transition-colors uf-p-3 uf-flex uf-items-center uf-justify-between ${!isSupported ? "uf-cursor-not-allowed uf-opacity-50" : ""}`,
56612
56743
  style: {
56613
56744
  backgroundColor: isSelected ? colors2.primary + "20" : components.card.backgroundColor,
56614
56745
  borderRadius: components.list.rowBorderRadius,
@@ -56698,9 +56829,9 @@ function CoinbaseConnect({
56698
56829
  "button",
56699
56830
  {
56700
56831
  onClick: () => {
56701
- if (selectedHolding) handleSelectAsset(selectedHolding);
56832
+ if (selectedHolding && selectedHoldingIsSupported) handleSelectAsset(selectedHolding);
56702
56833
  },
56703
- disabled: !selectedHolding || isLoading,
56834
+ disabled: !selectedHolding || isLoading || supportedTokensLoading || exchangesLoading || !selectedHoldingIsSupported,
56704
56835
  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",
56705
56836
  style: {
56706
56837
  backgroundColor: colors2.primary,
@@ -56724,22 +56855,16 @@ function CoinbaseConnect({
56724
56855
  const isValidAmount = inputUsdNum > 0 && (maxUsdAmount == null || inputUsdNum <= maxUsdAmount) && inputUsdNum >= minDepositUsd;
56725
56856
  const displayValue = sendAmount || "0";
56726
56857
  const formattedBalance = formatCryptoAmount(selectedAsset.amount, selectedAsset.currency);
56727
- const balanceDisplay = maxUsdAmount != null && maxUsdAmount > 0 ? `$${maxUsdAmount.toLocaleString(void 0, { minimumFractionDigits: 2, maximumFractionDigits: 2 })} (${formattedBalance})` : formattedBalance;
56858
+ const balanceSubtitle = maxUsdAmount != null && maxUsdAmount > 0 ? `Balance: $${maxUsdAmount.toLocaleString(void 0, { minimumFractionDigits: 2, maximumFractionDigits: 2 })} (${formattedBalance})` : `Balance: ${formattedBalance}`;
56728
56859
  return /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)(import_jsx_runtime48.Fragment, { children: [
56729
56860
  /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
56730
56861
  DepositHeader,
56731
56862
  {
56732
56863
  title: "Enter Amount",
56864
+ subtitle: balanceSubtitle,
56733
56865
  showBack: true,
56734
56866
  onBack: handleBack,
56735
- onClose,
56736
- showBalance: true,
56737
- balanceAddress: recipientAddress,
56738
- balanceChainType: destinationChainType === "ethereum" || destinationChainType === "solana" || destinationChainType === "bitcoin" ? destinationChainType : void 0,
56739
- balanceChainId: destinationChainId,
56740
- balanceTokenAddress: destinationTokenAddress,
56741
- projectName: projectConfig?.project_name,
56742
- publishableKey
56867
+ onClose
56743
56868
  }
56744
56869
  ),
56745
56870
  /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("div", { className: "uf-flex uf-flex-col uf-pb-4", style: viewTransitionStyle, children: [
@@ -56861,7 +56986,7 @@ function CoinbaseConnect({
56861
56986
  "button",
56862
56987
  {
56863
56988
  onClick: handleCreateTransfer,
56864
- disabled: !isValidAmount || isLoading,
56989
+ disabled: !isValidAmount || isLoading || defaultTokenLoading || !defaultTokenData,
56865
56990
  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",
56866
56991
  style: {
56867
56992
  backgroundColor: colors2.primary,
@@ -56870,7 +56995,7 @@ function CoinbaseConnect({
56870
56995
  borderRadius: components.button.borderRadius,
56871
56996
  border: `${components.button.borderWidth}px solid ${components.button.borderColor}`
56872
56997
  },
56873
- children: isLoading ? /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(LoaderCircle, { className: "uf-w-4 uf-h-4 uf-animate-spin" }) : "Review"
56998
+ children: isLoading || defaultTokenLoading ? /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(LoaderCircle, { className: "uf-w-4 uf-h-4 uf-animate-spin" }) : "Review"
56874
56999
  }
56875
57000
  )
56876
57001
  ] })
@@ -57503,39 +57628,6 @@ function useAddressValidation({
57503
57628
  error: error ?? null
57504
57629
  };
57505
57630
  }
57506
- function useSupportedDepositTokens(publishableKey, options2) {
57507
- const hasDestination = options2?.destination_token_address && options2?.destination_chain_id && options2?.destination_chain_type;
57508
- const filteredOptions = {
57509
- ...hasDestination ? {
57510
- destination_token_address: options2.destination_token_address,
57511
- destination_chain_id: options2.destination_chain_id,
57512
- destination_chain_type: options2.destination_chain_type
57513
- } : {},
57514
- ...options2?.product_type ? { product_type: options2.product_type } : {}
57515
- };
57516
- const hasFilteredOptions = Object.keys(filteredOptions).length > 0;
57517
- return useQuery({
57518
- queryKey: [
57519
- "unifold",
57520
- "supportedDepositTokens",
57521
- publishableKey,
57522
- filteredOptions?.destination_token_address ?? null,
57523
- filteredOptions?.destination_chain_id ?? null,
57524
- filteredOptions?.destination_chain_type ?? null,
57525
- filteredOptions?.product_type ?? null
57526
- ],
57527
- queryFn: () => getSupportedDepositTokens(
57528
- publishableKey,
57529
- hasFilteredOptions ? filteredOptions : void 0
57530
- ),
57531
- staleTime: 1e3 * 60 * 5,
57532
- // 5 minutes — token list rarely changes
57533
- gcTime: 1e3 * 60 * 30,
57534
- // 30 minutes in cache
57535
- refetchOnMount: false,
57536
- refetchOnWindowFocus: false
57537
- });
57538
- }
57539
57631
  function ThemeStyleInjector({
57540
57632
  children,
57541
57633
  className
@@ -59920,7 +60012,7 @@ function SelectTokenView({
59920
60012
  ),
59921
60013
  walletInfoProp ? /* @__PURE__ */ (0, import_jsx_runtime69.jsx)("div", { className: "uf-flex uf-w-full uf-justify-center uf-mb-6", children: /* @__PURE__ */ (0, import_jsx_runtime69.jsx)(WalletWithNetworkBadge, { walletInfo: walletInfoProp }) }) : null,
59922
60014
  /* @__PURE__ */ (0, import_jsx_runtime69.jsxs)("div", { className: "uf-flex uf-min-h-0 uf-flex-1 uf-flex-col", children: [
59923
- /* @__PURE__ */ (0, import_jsx_runtime69.jsx)("div", { className: "uf-h-[220px] uf-shrink-0 uf-overflow-y-auto [scrollbar-width:none] [&::-webkit-scrollbar]:uf-hidden", children: /* @__PURE__ */ (0, import_jsx_runtime69.jsx)("div", { className: "uf-space-y-2", children: isLoading ? /* @__PURE__ */ (0, import_jsx_runtime69.jsx)("div", { className: "uf-flex uf-items-center uf-justify-center uf-py-12", children: /* @__PURE__ */ (0, import_jsx_runtime69.jsx)(
60015
+ /* @__PURE__ */ (0, import_jsx_runtime69.jsx)("div", { className: "uf-h-[300px] uf-shrink-0 uf-overflow-y-auto [scrollbar-width:none] [&::-webkit-scrollbar]:uf-hidden", children: /* @__PURE__ */ (0, import_jsx_runtime69.jsx)("div", { className: "uf-space-y-2", children: isLoading ? /* @__PURE__ */ (0, import_jsx_runtime69.jsx)("div", { className: "uf-flex uf-items-center uf-justify-center uf-py-12", children: /* @__PURE__ */ (0, import_jsx_runtime69.jsx)(
59924
60016
  LoaderCircle,
59925
60017
  {
59926
60018
  className: "uf-w-6 uf-h-6 uf-animate-spin",
@@ -62359,11 +62451,12 @@ function DepositModal({
62359
62451
  return sum + (isNaN(usd) ? 0 : usd);
62360
62452
  }, 0);
62361
62453
  const balanceUsd = totalUsd > 0 ? totalUsd.toLocaleString(void 0, { minimumFractionDigits: 2, maximumFractionDigits: 2 }) : null;
62362
- setConnectedExchange((prev) => ({ ...prev, balanceUsd, isLoading: false }));
62454
+ setConnectedExchange((prev) => prev ? { ...prev, balanceUsd, isLoading: false } : null);
62363
62455
  };
62364
62456
  getIntegrationHoldings(IntegrationProvider.COINBASE, stored.access_token, publishableKey).then(processHoldings).catch(async () => {
62365
62457
  try {
62366
62458
  const refreshResult = await refreshIntegrationToken(stored.access_token, publishableKey);
62459
+ if (!getStoredIntegrationToken(IntegrationProvider.COINBASE)) return;
62367
62460
  setStoredIntegrationToken({
62368
62461
  integration_provider: IntegrationProvider.COINBASE,
62369
62462
  access_token: refreshResult.access_token,
@@ -62372,6 +62465,7 @@ function DepositModal({
62372
62465
  const retryResult = await getIntegrationHoldings(IntegrationProvider.COINBASE, refreshResult.access_token, publishableKey);
62373
62466
  processHoldings(retryResult);
62374
62467
  } catch {
62468
+ if (!getStoredIntegrationToken(IntegrationProvider.COINBASE)) return;
62375
62469
  clearStoredIntegrationToken(IntegrationProvider.COINBASE);
62376
62470
  setConnectedExchange(null);
62377
62471
  }
@@ -62539,6 +62633,14 @@ function DepositModal({
62539
62633
  setBrowserWalletInfo(null);
62540
62634
  setBrowserWalletModalOpen(false);
62541
62635
  };
62636
+ const handleExchangeDisconnect = () => {
62637
+ const stored = getStoredIntegrationToken(IntegrationProvider.COINBASE);
62638
+ if (stored) {
62639
+ revokeIntegrationToken(stored.access_token, publishableKey);
62640
+ }
62641
+ clearStoredIntegrationToken(IntegrationProvider.COINBASE);
62642
+ setConnectedExchange(null);
62643
+ };
62542
62644
  const handleClose = () => {
62543
62645
  onOpenChange(false);
62544
62646
  if (resetViewTimeoutRef.current) {
@@ -62744,6 +62846,7 @@ function DepositModal({
62744
62846
  setCoinbaseSkipToHoldings(true);
62745
62847
  setView("coinbase_connect");
62746
62848
  },
62849
+ onDisconnect: handleExchangeDisconnect,
62747
62850
  title: i18n2.connectExchange.title,
62748
62851
  subtitle: i18n2.connectExchange.subtitle,
62749
62852
  exchanges: integrationExchanges,
@@ -62948,34 +63051,37 @@ function DepositModal({
62948
63051
  ),
62949
63052
  depositPoweredByFooter
62950
63053
  ] })
62951
- ] }) : view === "coinbase_connect" ? /* @__PURE__ */ (0, import_jsx_runtime75.jsx)(
62952
- CoinbaseConnect,
62953
- {
62954
- publishableKey,
62955
- userId,
62956
- wallets,
62957
- recipientAddress,
62958
- destinationTokenAddress: destinationTokenAddress ?? "",
62959
- destinationChainId: destinationChainId ?? "",
62960
- destinationChainType: destinationChainType ?? "",
62961
- onTransferSuccess: (result) => {
62962
- onDepositSuccess?.({
62963
- message: "Transfer completed via Coinbase Connect",
62964
- transaction: result
62965
- });
62966
- },
62967
- onTransferError: (error) => {
62968
- onDepositError?.({
62969
- message: error.message,
62970
- error
62971
- });
62972
- },
62973
- onBack: handleBack,
62974
- onClose: handleClose,
62975
- skipToHoldings: coinbaseSkipToHoldings,
62976
- onExecutionsChange: setDepositExecutions
62977
- }
62978
- ) : view === "cashapp" ? /* @__PURE__ */ (0, import_jsx_runtime75.jsxs)(import_jsx_runtime75.Fragment, { children: [
63054
+ ] }) : view === "coinbase_connect" ? /* @__PURE__ */ (0, import_jsx_runtime75.jsxs)("div", { className: "uf-flex uf-flex-col uf-gap-1.5", children: [
63055
+ /* @__PURE__ */ (0, import_jsx_runtime75.jsx)(
63056
+ CoinbaseConnect,
63057
+ {
63058
+ publishableKey,
63059
+ userId,
63060
+ wallets,
63061
+ recipientAddress,
63062
+ destinationTokenAddress: destinationTokenAddress ?? "",
63063
+ destinationChainId: destinationChainId ?? "",
63064
+ destinationChainType: destinationChainType ?? "",
63065
+ onTransferSuccess: (result) => {
63066
+ onDepositSuccess?.({
63067
+ message: "Transfer completed via Coinbase Connect",
63068
+ transaction: result
63069
+ });
63070
+ },
63071
+ onTransferError: (error) => {
63072
+ onDepositError?.({
63073
+ message: error.message,
63074
+ error
63075
+ });
63076
+ },
63077
+ onBack: handleBack,
63078
+ onClose: handleClose,
63079
+ skipToHoldings: coinbaseSkipToHoldings,
63080
+ onExecutionsChange: setDepositExecutions
63081
+ }
63082
+ ),
63083
+ depositPoweredByFooter
63084
+ ] }) : view === "cashapp" ? /* @__PURE__ */ (0, import_jsx_runtime75.jsxs)(import_jsx_runtime75.Fragment, { children: [
62979
63085
  /* @__PURE__ */ (0, import_jsx_runtime75.jsx)(
62980
63086
  DepositHeader,
62981
63087
  {