@unifold/ui-react 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.mjs CHANGED
@@ -5,7 +5,7 @@ import {
5
5
  useLayoutEffect as useLayoutEffect2,
6
6
  useCallback as useCallback6,
7
7
  useRef as useRef8,
8
- useMemo as useMemo9
8
+ useMemo as useMemo10
9
9
  } from "react";
10
10
  import { ChevronRight as ChevronRight14, MapPinOff, AlertTriangle as AlertTriangle2 } from "lucide-react";
11
11
 
@@ -1431,7 +1431,8 @@ var en_default = {
1431
1431
  title: "Transfer Failed",
1432
1432
  tryAgain: "Try Again"
1433
1433
  },
1434
- continue: "Continue"
1434
+ continue: "Continue",
1435
+ disconnect: "Disconnect"
1435
1436
  },
1436
1437
  buyWithCard: {
1437
1438
  onramp: {
@@ -4786,6 +4787,7 @@ import { Link2, ChevronRight as ChevronRight7 } from "lucide-react";
4786
4787
  import { jsx as jsx19, jsxs as jsxs16 } from "react/jsx-runtime";
4787
4788
  function ConnectExchangeButton({
4788
4789
  onClick,
4790
+ onDisconnect,
4789
4791
  title,
4790
4792
  subtitle,
4791
4793
  exchanges,
@@ -4798,6 +4800,109 @@ function ConnectExchangeButton({
4798
4800
  setIsTouchDevice("ontouchstart" in window || navigator.maxTouchPoints > 0);
4799
4801
  }, []);
4800
4802
  const isConnected = connectedExchange != null;
4803
+ const handleDisconnectClick = (e) => {
4804
+ e.preventDefault();
4805
+ e.stopPropagation();
4806
+ onDisconnect?.();
4807
+ };
4808
+ const rowSurfaceStyle = {
4809
+ backgroundColor: isHovered ? colors2.cardHover : components.card.backgroundColor,
4810
+ borderRadius: components.card.borderRadius,
4811
+ border: `${components.card.borderWidth}px solid ${components.card.borderColor}`
4812
+ };
4813
+ const iconBlock = isConnected ? connectedExchange.iconUrl ? /* @__PURE__ */ jsx19(
4814
+ "img",
4815
+ {
4816
+ src: connectedExchange.iconUrl,
4817
+ alt: connectedExchange.name,
4818
+ width: 36,
4819
+ height: 36,
4820
+ className: "uf-rounded-lg"
4821
+ }
4822
+ ) : /* @__PURE__ */ jsx19(
4823
+ "div",
4824
+ {
4825
+ className: "uf-w-9 uf-h-9 uf-rounded-lg uf-flex uf-items-center uf-justify-center",
4826
+ style: { backgroundColor: colors2.card },
4827
+ children: /* @__PURE__ */ jsx19(
4828
+ "span",
4829
+ {
4830
+ className: "uf-text-xs uf-font-medium",
4831
+ style: { color: components.card.iconColor },
4832
+ children: connectedExchange.name.slice(0, 2).toUpperCase()
4833
+ }
4834
+ )
4835
+ }
4836
+ ) : /* @__PURE__ */ jsx19("div", { className: "uf-rounded-lg uf-p-2", children: /* @__PURE__ */ jsx19(
4837
+ Link2,
4838
+ {
4839
+ className: "uf-w-5 uf-h-5",
4840
+ style: { color: components.card.iconColor }
4841
+ }
4842
+ ) });
4843
+ const titleSubtitleBlock = /* @__PURE__ */ jsxs16("div", { className: "uf-text-left", children: [
4844
+ /* @__PURE__ */ jsx19(
4845
+ "div",
4846
+ {
4847
+ className: "uf-text-sm uf-font-light uf-mb-0.5",
4848
+ style: {
4849
+ color: components.card.titleColor,
4850
+ fontFamily: fonts.regular
4851
+ },
4852
+ children: isConnected ? connectedExchange.name : title
4853
+ }
4854
+ ),
4855
+ isConnected && connectedExchange.isLoading ? /* @__PURE__ */ jsx19("div", { className: "uf-h-3 uf-w-24 uf-bg-muted uf-rounded uf-animate-pulse" }) : /* @__PURE__ */ jsx19(
4856
+ "div",
4857
+ {
4858
+ className: "uf-text-xs uf-font-light",
4859
+ style: {
4860
+ color: components.card.subtitleColor,
4861
+ fontFamily: fonts.regular
4862
+ },
4863
+ children: isConnected ? connectedExchange.balanceUsd ? `$${connectedExchange.balanceUsd} \u2022 2 min` : "Deposit from exchange" : subtitle
4864
+ }
4865
+ )
4866
+ ] });
4867
+ if (isConnected && onDisconnect) {
4868
+ return /* @__PURE__ */ jsxs16(
4869
+ "div",
4870
+ {
4871
+ onMouseEnter: () => !isTouchDevice && setIsHovered(true),
4872
+ onMouseLeave: () => setIsHovered(false),
4873
+ onTouchStart: () => setIsHovered(false),
4874
+ className: "uf-w-full uf-transition-colors uf-flex uf-items-stretch uf-group",
4875
+ style: rowSurfaceStyle,
4876
+ children: [
4877
+ /* @__PURE__ */ jsxs16(
4878
+ "button",
4879
+ {
4880
+ type: "button",
4881
+ onClick,
4882
+ 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",
4883
+ children: [
4884
+ iconBlock,
4885
+ titleSubtitleBlock
4886
+ ]
4887
+ }
4888
+ ),
4889
+ /* @__PURE__ */ jsx19("div", { className: "uf-flex uf-items-center uf-gap-1 uf-shrink-0 uf-pr-2 uf-pl-0", children: /* @__PURE__ */ jsx19(
4890
+ "button",
4891
+ {
4892
+ type: "button",
4893
+ onClick: handleDisconnectClick,
4894
+ 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",
4895
+ style: {
4896
+ color: colors2.error,
4897
+ fontFamily: fonts.regular
4898
+ },
4899
+ children: i18n.connectExchange.disconnect
4900
+ }
4901
+ ) })
4902
+ ]
4903
+ }
4904
+ );
4905
+ }
4801
4906
  return /* @__PURE__ */ jsxs16(
4802
4907
  "button",
4803
4908
  {
@@ -4806,67 +4911,11 @@ function ConnectExchangeButton({
4806
4911
  onMouseLeave: () => setIsHovered(false),
4807
4912
  onTouchStart: () => setIsHovered(false),
4808
4913
  className: "uf-w-full uf-transition-colors uf-p-3 uf-flex uf-items-center uf-justify-between uf-group",
4809
- style: {
4810
- backgroundColor: isHovered ? colors2.cardHover : components.card.backgroundColor,
4811
- borderRadius: components.card.borderRadius,
4812
- border: `${components.card.borderWidth}px solid ${components.card.borderColor}`
4813
- },
4914
+ style: rowSurfaceStyle,
4814
4915
  children: [
4815
4916
  /* @__PURE__ */ jsxs16("div", { className: "uf-flex uf-items-center uf-gap-3", children: [
4816
- isConnected ? connectedExchange.iconUrl ? /* @__PURE__ */ jsx19(
4817
- "img",
4818
- {
4819
- src: connectedExchange.iconUrl,
4820
- alt: connectedExchange.name,
4821
- width: 36,
4822
- height: 36,
4823
- className: "uf-rounded-lg"
4824
- }
4825
- ) : /* @__PURE__ */ jsx19(
4826
- "div",
4827
- {
4828
- className: "uf-w-9 uf-h-9 uf-rounded-lg uf-flex uf-items-center uf-justify-center",
4829
- style: { backgroundColor: colors2.card },
4830
- children: /* @__PURE__ */ jsx19(
4831
- "span",
4832
- {
4833
- className: "uf-text-xs uf-font-medium",
4834
- style: { color: components.card.iconColor },
4835
- children: connectedExchange.name.slice(0, 2).toUpperCase()
4836
- }
4837
- )
4838
- }
4839
- ) : /* @__PURE__ */ jsx19("div", { className: "uf-rounded-lg uf-p-2", children: /* @__PURE__ */ jsx19(
4840
- Link2,
4841
- {
4842
- className: "uf-w-5 uf-h-5",
4843
- style: { color: components.card.iconColor }
4844
- }
4845
- ) }),
4846
- /* @__PURE__ */ jsxs16("div", { className: "uf-text-left", children: [
4847
- /* @__PURE__ */ jsx19(
4848
- "div",
4849
- {
4850
- className: "uf-text-sm uf-font-light uf-mb-0.5",
4851
- style: {
4852
- color: components.card.titleColor,
4853
- fontFamily: fonts.regular
4854
- },
4855
- children: isConnected ? connectedExchange.name : title
4856
- }
4857
- ),
4858
- isConnected && connectedExchange.isLoading ? /* @__PURE__ */ jsx19("div", { className: "uf-h-3 uf-w-24 uf-bg-muted uf-rounded uf-animate-pulse" }) : /* @__PURE__ */ jsx19(
4859
- "div",
4860
- {
4861
- className: "uf-text-xs uf-font-light",
4862
- style: {
4863
- color: components.card.subtitleColor,
4864
- fontFamily: fonts.regular
4865
- },
4866
- children: isConnected ? connectedExchange.balanceUsd ? `$${connectedExchange.balanceUsd} \u2022 2 min` : "Deposit from exchange" : subtitle
4867
- }
4868
- )
4869
- ] })
4917
+ iconBlock,
4918
+ titleSubtitleBlock
4870
4919
  ] }),
4871
4920
  /* @__PURE__ */ jsxs16("div", { className: "uf-flex uf-items-center uf-gap-1.5", children: [
4872
4921
  !isConnected && exchanges && exchanges.length > 0 ? exchanges.slice(0, 4).map((ex, i) => {
@@ -7736,7 +7785,7 @@ function BrowserWalletButton({
7736
7785
  }
7737
7786
 
7738
7787
  // src/components/deposits/CoinbaseConnect.tsx
7739
- import { useState as useState20, useEffect as useEffect16, useCallback as useCallback2, useRef as useRef5 } from "react";
7788
+ import { useState as useState20, useEffect as useEffect16, useCallback as useCallback2, useMemo as useMemo3, useRef as useRef5 } from "react";
7740
7789
  import {
7741
7790
  ChevronRight as ChevronRight11,
7742
7791
  ChevronDown as ChevronDown3,
@@ -7755,8 +7804,6 @@ import {
7755
7804
  getIntegrationHoldings,
7756
7805
  createIntegrationTransfer,
7757
7806
  confirmIntegrationTransfer,
7758
- getIntegrationTransferDefaultToken,
7759
- getSupportedDepositTokens,
7760
7807
  IntegrationProvider
7761
7808
  } from "@unifold/core";
7762
7809
 
@@ -7778,6 +7825,77 @@ function useProjectConfig({
7778
7825
  return { projectConfig, isLoading };
7779
7826
  }
7780
7827
 
7828
+ // src/hooks/use-supported-deposit-tokens.ts
7829
+ import { useQuery as useQuery5 } from "@tanstack/react-query";
7830
+ import {
7831
+ getSupportedDepositTokens
7832
+ } from "@unifold/core";
7833
+ function useSupportedDepositTokens(publishableKey, options) {
7834
+ const hasDestination = options?.destination_token_address && options?.destination_chain_id && options?.destination_chain_type;
7835
+ const filteredOptions = {
7836
+ ...hasDestination ? {
7837
+ destination_token_address: options.destination_token_address,
7838
+ destination_chain_id: options.destination_chain_id,
7839
+ destination_chain_type: options.destination_chain_type
7840
+ } : {},
7841
+ ...options?.product_type ? { product_type: options.product_type } : {}
7842
+ };
7843
+ const hasFilteredOptions = Object.keys(filteredOptions).length > 0;
7844
+ return useQuery5({
7845
+ queryKey: [
7846
+ "unifold",
7847
+ "supportedDepositTokens",
7848
+ publishableKey,
7849
+ filteredOptions?.destination_token_address ?? null,
7850
+ filteredOptions?.destination_chain_id ?? null,
7851
+ filteredOptions?.destination_chain_type ?? null,
7852
+ filteredOptions?.product_type ?? null
7853
+ ],
7854
+ queryFn: () => getSupportedDepositTokens(
7855
+ publishableKey,
7856
+ hasFilteredOptions ? filteredOptions : void 0
7857
+ ),
7858
+ staleTime: 1e3 * 60 * 5,
7859
+ // 5 minutes — token list rarely changes
7860
+ gcTime: 1e3 * 60 * 30,
7861
+ // 30 minutes in cache
7862
+ refetchOnMount: false,
7863
+ refetchOnWindowFocus: false
7864
+ });
7865
+ }
7866
+
7867
+ // src/hooks/use-integration-transfer-default-token.ts
7868
+ import { useQuery as useQuery6 } from "@tanstack/react-query";
7869
+ import {
7870
+ getIntegrationTransferDefaultToken
7871
+ } from "@unifold/core";
7872
+ function useIntegrationTransferDefaultToken({
7873
+ params,
7874
+ publishableKey,
7875
+ enabled = true
7876
+ }) {
7877
+ return useQuery6({
7878
+ queryKey: [
7879
+ "unifold",
7880
+ "integrationTransferDefaultToken",
7881
+ publishableKey,
7882
+ params?.integration_provider ?? null,
7883
+ params?.source_currency ?? null,
7884
+ params?.destination_token_address ?? null,
7885
+ params?.destination_chain_id ?? null,
7886
+ params?.destination_chain_type ?? null,
7887
+ params?.country_code ?? null,
7888
+ params?.subdivision_code ?? null
7889
+ ],
7890
+ queryFn: () => getIntegrationTransferDefaultToken(params, publishableKey),
7891
+ enabled: enabled && !!params,
7892
+ staleTime: 1e3 * 60 * 5,
7893
+ gcTime: 1e3 * 60 * 30,
7894
+ refetchOnMount: false,
7895
+ refetchOnWindowFocus: false
7896
+ });
7897
+ }
7898
+
7781
7899
  // src/lib/integrations.ts
7782
7900
  var INTEGRATIONS_STORAGE_KEY = "unifold:integrations:connected";
7783
7901
  function getStoredIntegrations() {
@@ -7826,6 +7944,23 @@ function CoinbaseConnect({
7826
7944
  const { colors: colors2, fonts, components } = useTheme();
7827
7945
  const { projectConfig } = useProjectConfig({ publishableKey });
7828
7946
  const { userIpInfo } = useUserIp();
7947
+ const { data: supportedTokensData, isLoading: supportedTokensLoading } = useSupportedDepositTokens(publishableKey, {
7948
+ destination_token_address: destinationTokenAddress,
7949
+ destination_chain_id: destinationChainId,
7950
+ destination_chain_type: destinationChainType
7951
+ });
7952
+ const supportedSymbols = useMemo3(() => {
7953
+ const set = /* @__PURE__ */ new Set();
7954
+ supportedTokensData?.data.forEach((token) => set.add(token.symbol.toLowerCase()));
7955
+ return set;
7956
+ }, [supportedTokensData]);
7957
+ const stablecoinSymbols = useMemo3(() => {
7958
+ const set = /* @__PURE__ */ new Set();
7959
+ supportedTokensData?.data.forEach((token) => {
7960
+ if (token.is_stablecoin) set.add(token.symbol.toLowerCase());
7961
+ });
7962
+ return set;
7963
+ }, [supportedTokensData]);
7829
7964
  const appName = projectConfig?.project_name ?? "Unifold";
7830
7965
  const t12 = i18n.connectExchange;
7831
7966
  const initialView = skipToHoldings && getStoredIntegrationToken(IntegrationProvider.COINBASE) ? "holdings" : "select_exchange";
@@ -7848,7 +7983,43 @@ function CoinbaseConnect({
7848
7983
  const [confirmResult, setConfirmResult] = useState20(null);
7849
7984
  const [showTransferDetails, setShowTransferDetails] = useState20(false);
7850
7985
  const [transferDepositWalletId, setTransferDepositWalletId] = useState20(void 0);
7851
- const [minDepositUsd, setMinDepositUsd] = useState20(0);
7986
+ const exchangeSupportedCurrencies = useMemo3(() => {
7987
+ const set = /* @__PURE__ */ new Set();
7988
+ selectedExchange?.supported_currencies.forEach((c) => set.add(c.toLowerCase()));
7989
+ return set;
7990
+ }, [selectedExchange]);
7991
+ const defaultTokenParams = useMemo3(
7992
+ () => selectedAsset ? {
7993
+ integration_provider: IntegrationProvider.COINBASE,
7994
+ source_currency: selectedAsset.currency.toLowerCase(),
7995
+ destination_token_address: destinationTokenAddress,
7996
+ destination_chain_id: destinationChainId,
7997
+ destination_chain_type: destinationChainType,
7998
+ country_code: userIpInfo?.alpha2,
7999
+ subdivision_code: userIpInfo?.subdivisionCode ?? void 0
8000
+ } : null,
8001
+ [selectedAsset, destinationTokenAddress, destinationChainId, destinationChainType, userIpInfo?.alpha2, userIpInfo?.subdivisionCode]
8002
+ );
8003
+ const { data: defaultTokenData, isLoading: defaultTokenLoading } = useIntegrationTransferDefaultToken({
8004
+ params: defaultTokenParams,
8005
+ publishableKey
8006
+ });
8007
+ const sortedHoldings = useMemo3(() => {
8008
+ const supported = [];
8009
+ const unsupported = [];
8010
+ holdings.forEach((account) => {
8011
+ const currencyLower = account.currency.toLowerCase();
8012
+ const isSupported = (supportedSymbols.size === 0 || supportedSymbols.has(currencyLower)) && (exchangeSupportedCurrencies.size === 0 || exchangeSupportedCurrencies.has(currencyLower));
8013
+ if (isSupported) supported.push(account);
8014
+ else unsupported.push(account);
8015
+ });
8016
+ return [...supported, ...unsupported];
8017
+ }, [holdings, supportedSymbols, exchangeSupportedCurrencies]);
8018
+ const selectedHoldingIsSupported = useMemo3(() => {
8019
+ if (!selectedHolding) return false;
8020
+ const currencyLower = selectedHolding.currency.toLowerCase();
8021
+ return (supportedSymbols.size === 0 || supportedSymbols.has(currencyLower)) && (exchangeSupportedCurrencies.size === 0 || exchangeSupportedCurrencies.has(currencyLower));
8022
+ }, [selectedHolding, supportedSymbols, exchangeSupportedCurrencies]);
7852
8023
  const exchangeName = selectedExchange?.service_provider_display_name || "Exchange";
7853
8024
  const {
7854
8025
  executions: depositExecutions,
@@ -7964,53 +8135,17 @@ function CoinbaseConnect({
7964
8135
  if (pollRef.current) clearInterval(pollRef.current);
7965
8136
  };
7966
8137
  }, []);
7967
- useEffect16(() => {
7968
- if (view !== "enter_amount" || !selectedAsset) return;
7969
- let cancelled = false;
7970
- const fetchMinDeposit = async () => {
7971
- try {
7972
- const [defaultToken, supportedTokens] = await Promise.all([
7973
- getIntegrationTransferDefaultToken(
7974
- {
7975
- integration_provider: IntegrationProvider.COINBASE,
7976
- source_currency: selectedAsset.currency.toLowerCase(),
7977
- destination_token_address: destinationTokenAddress,
7978
- destination_chain_id: destinationChainId,
7979
- destination_chain_type: destinationChainType,
7980
- country_code: userIpInfo?.alpha2,
7981
- subdivision_code: userIpInfo?.subdivisionCode ?? void 0
7982
- },
7983
- publishableKey
7984
- ),
7985
- getSupportedDepositTokens(publishableKey, {
7986
- destination_token_address: destinationTokenAddress,
7987
- destination_chain_id: destinationChainId,
7988
- destination_chain_type: destinationChainType
7989
- })
7990
- ]);
7991
- if (cancelled) return;
7992
- const supportedToken = supportedTokens.data.find(
7993
- (t13) => t13.symbol.toLowerCase() === selectedAsset.currency.toLowerCase()
7994
- );
7995
- if (supportedToken && supportedToken.chains.length > 0) {
7996
- const matchingChain = supportedToken.chains.find(
7997
- (c) => c.chain_name.toLowerCase() === defaultToken.source_network.toLowerCase()
7998
- );
7999
- setMinDepositUsd(
8000
- matchingChain?.minimum_deposit_amount_usd ?? Math.min(...supportedToken.chains.map((c) => c.minimum_deposit_amount_usd))
8001
- );
8002
- } else {
8003
- setMinDepositUsd(0);
8004
- }
8005
- } catch {
8006
- if (!cancelled) setMinDepositUsd(0);
8007
- }
8008
- };
8009
- fetchMinDeposit();
8010
- return () => {
8011
- cancelled = true;
8012
- };
8013
- }, [view, selectedAsset, publishableKey, destinationTokenAddress, destinationChainId, destinationChainType, userIpInfo?.alpha2, userIpInfo?.subdivisionCode]);
8138
+ const minDepositUsd = useMemo3(() => {
8139
+ if (!selectedAsset || !defaultTokenData) return 0;
8140
+ const supportedToken = supportedTokensData?.data.find(
8141
+ (t13) => t13.symbol.toLowerCase() === selectedAsset.currency.toLowerCase()
8142
+ );
8143
+ if (!supportedToken || supportedToken.chains.length === 0) return 0;
8144
+ const matchingChain = supportedToken.chains.find(
8145
+ (c) => c.chain_name.toLowerCase() === defaultTokenData.source_network.toLowerCase()
8146
+ );
8147
+ return matchingChain?.minimum_deposit_amount_usd ?? Math.min(...supportedToken.chains.map((c) => c.minimum_deposit_amount_usd));
8148
+ }, [selectedAsset, supportedTokensData, defaultTokenData]);
8014
8149
  const handleSelectExchange = (exchange) => {
8015
8150
  if (!exchange.enabled) return;
8016
8151
  setSelectedExchange(exchange);
@@ -8055,7 +8190,6 @@ function CoinbaseConnect({
8055
8190
  const handleSelectAsset = (asset) => {
8056
8191
  setSelectedAsset(asset);
8057
8192
  setSendAmount("");
8058
- setMinDepositUsd(0);
8059
8193
  transitionTo("enter_amount");
8060
8194
  };
8061
8195
  const handleCreateTransfer = async () => {
@@ -8067,18 +8201,10 @@ function CoinbaseConnect({
8067
8201
  const cryptoAmount = (usdAmount / rate).toFixed(8);
8068
8202
  setIsLoading(true);
8069
8203
  try {
8070
- const { source_network, source_chain_type } = await getIntegrationTransferDefaultToken(
8071
- {
8072
- integration_provider: IntegrationProvider.COINBASE,
8073
- source_currency: selectedAsset.currency.toLowerCase(),
8074
- destination_token_address: destinationTokenAddress,
8075
- destination_chain_id: destinationChainId,
8076
- destination_chain_type: destinationChainType,
8077
- country_code: userIpInfo?.alpha2,
8078
- subdivision_code: userIpInfo?.subdivisionCode ?? void 0
8079
- },
8080
- publishableKey
8081
- );
8204
+ if (!defaultTokenData) {
8205
+ throw new Error("Transfer details not ready. Please try again.");
8206
+ }
8207
+ const { source_network, source_chain_type } = defaultTokenData;
8082
8208
  const matchingWallet = wallets.find((w) => w.chain_type === source_chain_type);
8083
8209
  const depositAddress = matchingWallet?.address ?? recipientAddress ?? "";
8084
8210
  if (!depositAddress) {
@@ -8230,13 +8356,12 @@ function CoinbaseConnect({
8230
8356
  break;
8231
8357
  }
8232
8358
  };
8233
- const STABLECOIN_SYMBOLS = /* @__PURE__ */ new Set(["USDC", "USDT", "DAI", "BUSD", "TUSD", "USDP", "GUSD", "FRAX", "LUSD", "PYUSD", "EURC", "USDS"]);
8234
8359
  const formatCryptoAmount = (amount, currency) => {
8235
8360
  const num = parseFloat(amount);
8236
8361
  if (isNaN(num)) return `${amount} ${currency.toUpperCase()}`;
8237
- const isStable = STABLECOIN_SYMBOLS.has(currency.toUpperCase());
8238
- const maxDecimals = isStable ? 2 : 8;
8239
- return `${num.toLocaleString(void 0, { minimumFractionDigits: isStable ? 2 : 0, maximumFractionDigits: maxDecimals })} ${currency.toUpperCase()}`;
8362
+ const isStable = stablecoinSymbols.has(currency.toLowerCase());
8363
+ const maxDecimals = isStable ? 2 : 6;
8364
+ return `${num.toLocaleString(void 0, { minimumFractionDigits: 2, maximumFractionDigits: maxDecimals })} ${currency.toUpperCase()}`;
8240
8365
  };
8241
8366
  const viewTransitionStyle = {
8242
8367
  opacity: isTransitioning ? 0 : 1,
@@ -8838,7 +8963,7 @@ function CoinbaseConnect({
8838
8963
  className: "uf-w-6 uf-h-6 uf-animate-spin",
8839
8964
  style: { color: colors2.foregroundMuted }
8840
8965
  }
8841
- ) }) : holdings.length === 0 ? /* @__PURE__ */ jsx37("div", { className: "uf-text-center uf-py-8", children: /* @__PURE__ */ jsx37(
8966
+ ) }) : sortedHoldings.length === 0 ? /* @__PURE__ */ jsx37("div", { className: "uf-text-center uf-py-8", children: /* @__PURE__ */ jsx37(
8842
8967
  "div",
8843
8968
  {
8844
8969
  className: "uf-text-sm",
@@ -8848,17 +8973,19 @@ function CoinbaseConnect({
8848
8973
  },
8849
8974
  children: "No holdings found"
8850
8975
  }
8851
- ) }) : holdings.map((account) => {
8976
+ ) }) : sortedHoldings.map((account) => {
8852
8977
  const iconUrl = account.icon_urls?.find((u) => u.format === "svg")?.url || account.icon_urls?.find((u) => u.format === "png")?.url || account.icon_url;
8853
- const isSelected = selectedHolding?.id === account.id;
8978
+ const currencyLower = account.currency.toLowerCase();
8979
+ const isSupported = (supportedSymbols.size === 0 || supportedSymbols.has(currencyLower)) && (exchangeSupportedCurrencies.size === 0 || exchangeSupportedCurrencies.has(currencyLower));
8980
+ const isSelected = isSupported && selectedHolding?.id === account.id;
8854
8981
  const usdValue = account.amount_usd ? parseFloat(account.amount_usd) : null;
8855
- const formattedUsd = usdValue != null && usdValue > 0 ? `$${usdValue.toLocaleString(void 0, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}` : null;
8982
+ 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";
8856
8983
  return /* @__PURE__ */ jsxs33(
8857
8984
  "button",
8858
8985
  {
8859
8986
  onClick: () => setSelectedHolding(account),
8860
- disabled: isLoading,
8861
- className: "uf-w-full uf-transition-colors uf-p-3 uf-flex uf-items-center uf-justify-between",
8987
+ disabled: isLoading || !isSupported,
8988
+ 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" : ""}`,
8862
8989
  style: {
8863
8990
  backgroundColor: isSelected ? colors2.primary + "20" : components.card.backgroundColor,
8864
8991
  borderRadius: components.list.rowBorderRadius,
@@ -8948,9 +9075,9 @@ function CoinbaseConnect({
8948
9075
  "button",
8949
9076
  {
8950
9077
  onClick: () => {
8951
- if (selectedHolding) handleSelectAsset(selectedHolding);
9078
+ if (selectedHolding && selectedHoldingIsSupported) handleSelectAsset(selectedHolding);
8952
9079
  },
8953
- disabled: !selectedHolding || isLoading,
9080
+ disabled: !selectedHolding || isLoading || supportedTokensLoading || exchangesLoading || !selectedHoldingIsSupported,
8954
9081
  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",
8955
9082
  style: {
8956
9083
  backgroundColor: colors2.primary,
@@ -8974,22 +9101,16 @@ function CoinbaseConnect({
8974
9101
  const isValidAmount = inputUsdNum > 0 && (maxUsdAmount == null || inputUsdNum <= maxUsdAmount) && inputUsdNum >= minDepositUsd;
8975
9102
  const displayValue = sendAmount || "0";
8976
9103
  const formattedBalance = formatCryptoAmount(selectedAsset.amount, selectedAsset.currency);
8977
- const balanceDisplay = maxUsdAmount != null && maxUsdAmount > 0 ? `$${maxUsdAmount.toLocaleString(void 0, { minimumFractionDigits: 2, maximumFractionDigits: 2 })} (${formattedBalance})` : formattedBalance;
9104
+ const balanceSubtitle = maxUsdAmount != null && maxUsdAmount > 0 ? `Balance: $${maxUsdAmount.toLocaleString(void 0, { minimumFractionDigits: 2, maximumFractionDigits: 2 })} (${formattedBalance})` : `Balance: ${formattedBalance}`;
8978
9105
  return /* @__PURE__ */ jsxs33(Fragment4, { children: [
8979
9106
  /* @__PURE__ */ jsx37(
8980
9107
  DepositHeader,
8981
9108
  {
8982
9109
  title: "Enter Amount",
9110
+ subtitle: balanceSubtitle,
8983
9111
  showBack: true,
8984
9112
  onBack: handleBack,
8985
- onClose,
8986
- showBalance: true,
8987
- balanceAddress: recipientAddress,
8988
- balanceChainType: destinationChainType === "ethereum" || destinationChainType === "solana" || destinationChainType === "bitcoin" ? destinationChainType : void 0,
8989
- balanceChainId: destinationChainId,
8990
- balanceTokenAddress: destinationTokenAddress,
8991
- projectName: projectConfig?.project_name,
8992
- publishableKey
9113
+ onClose
8993
9114
  }
8994
9115
  ),
8995
9116
  /* @__PURE__ */ jsxs33("div", { className: "uf-flex uf-flex-col uf-pb-4", style: viewTransitionStyle, children: [
@@ -9111,7 +9232,7 @@ function CoinbaseConnect({
9111
9232
  "button",
9112
9233
  {
9113
9234
  onClick: handleCreateTransfer,
9114
- disabled: !isValidAmount || isLoading,
9235
+ disabled: !isValidAmount || isLoading || defaultTokenLoading || !defaultTokenData,
9115
9236
  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",
9116
9237
  style: {
9117
9238
  backgroundColor: colors2.primary,
@@ -9120,7 +9241,7 @@ function CoinbaseConnect({
9120
9241
  borderRadius: components.button.borderRadius,
9121
9242
  border: `${components.button.borderWidth}px solid ${components.button.borderColor}`
9122
9243
  },
9123
- children: isLoading ? /* @__PURE__ */ jsx37(Loader24, { className: "uf-w-4 uf-h-4 uf-animate-spin" }) : "Review"
9244
+ children: isLoading || defaultTokenLoading ? /* @__PURE__ */ jsx37(Loader24, { className: "uf-w-4 uf-h-4 uf-animate-spin" }) : "Review"
9124
9245
  }
9125
9246
  )
9126
9247
  ] })
@@ -9589,13 +9710,13 @@ function CoinbaseConnect({
9589
9710
  CoinbaseConnect.displayName = "CoinbaseConnect";
9590
9711
 
9591
9712
  // src/hooks/use-exchanges.ts
9592
- import { useQuery as useQuery5 } from "@tanstack/react-query";
9713
+ import { useQuery as useQuery7 } from "@tanstack/react-query";
9593
9714
  import { getExchanges } from "@unifold/core";
9594
9715
  function useExchanges({
9595
9716
  publishableKey,
9596
9717
  enabled = true
9597
9718
  }) {
9598
- const { data: exchanges = [], isLoading } = useQuery5({
9719
+ const { data: exchanges = [], isLoading } = useQuery7({
9599
9720
  queryKey: ["unifold", "exchanges", publishableKey],
9600
9721
  queryFn: () => getExchanges(void 0, publishableKey).then((res) => res.data),
9601
9722
  enabled,
@@ -9607,7 +9728,7 @@ function useExchanges({
9607
9728
  }
9608
9729
 
9609
9730
  // src/hooks/use-default-onramp-token.ts
9610
- import { useQuery as useQuery6 } from "@tanstack/react-query";
9731
+ import { useQuery as useQuery8 } from "@tanstack/react-query";
9611
9732
  import {
9612
9733
  getDefaultOnrampToken as getDefaultOnrampToken2
9613
9734
  } from "@unifold/core";
@@ -9620,7 +9741,7 @@ function useDefaultOnrampToken({
9620
9741
  subdivisionCode,
9621
9742
  enabled = true
9622
9743
  }) {
9623
- const { data: defaultToken, isLoading } = useQuery6({
9744
+ const { data: defaultToken, isLoading } = useQuery8({
9624
9745
  queryKey: [
9625
9746
  "unifold",
9626
9747
  "defaultOnrampToken",
@@ -9655,12 +9776,13 @@ import {
9655
9776
  getIntegrationExchanges as getIntegrationExchanges2,
9656
9777
  getIntegrationHoldings as getIntegrationHoldings2,
9657
9778
  refreshIntegrationToken as refreshIntegrationToken2,
9779
+ revokeIntegrationToken,
9658
9780
  IntegrationProvider as IntegrationProvider2,
9659
9781
  ActionType as ActionType3
9660
9782
  } from "@unifold/core";
9661
9783
 
9662
9784
  // src/hooks/use-allowed-country.ts
9663
- import { useQuery as useQuery7 } from "@tanstack/react-query";
9785
+ import { useQuery as useQuery9 } from "@tanstack/react-query";
9664
9786
  import {
9665
9787
  getIpAddress as getIpAddress2,
9666
9788
  getProjectConfig as getProjectConfig2
@@ -9670,7 +9792,7 @@ function useAllowedCountry(publishableKey) {
9670
9792
  data: ipData,
9671
9793
  isLoading: isIpLoading,
9672
9794
  error: ipError
9673
- } = useQuery7({
9795
+ } = useQuery9({
9674
9796
  queryKey: ["unifold", "ipAddress"],
9675
9797
  queryFn: () => getIpAddress2(),
9676
9798
  refetchOnMount: false,
@@ -9685,7 +9807,7 @@ function useAllowedCountry(publishableKey) {
9685
9807
  data: configData,
9686
9808
  isLoading: isConfigLoading,
9687
9809
  error: configError
9688
- } = useQuery7({
9810
+ } = useQuery9({
9689
9811
  queryKey: ["unifold", "projectConfig", publishableKey],
9690
9812
  queryFn: () => getProjectConfig2(publishableKey),
9691
9813
  refetchOnMount: false,
@@ -9728,7 +9850,7 @@ function useAllowedCountry(publishableKey) {
9728
9850
  }
9729
9851
 
9730
9852
  // src/hooks/use-address-validation.ts
9731
- import { useQuery as useQuery8 } from "@tanstack/react-query";
9853
+ import { useQuery as useQuery10 } from "@tanstack/react-query";
9732
9854
  import {
9733
9855
  verifyRecipientAddress
9734
9856
  } from "@unifold/core";
@@ -9742,7 +9864,7 @@ function useAddressValidation({
9742
9864
  refetchOnMount = false
9743
9865
  }) {
9744
9866
  const shouldValidate = enabled && !!recipientAddress && !!destinationChainType && !!destinationChainId && !!destinationTokenAddress;
9745
- const { data, isLoading, error } = useQuery8({
9867
+ const { data, isLoading, error } = useQuery10({
9746
9868
  queryKey: [
9747
9869
  "unifold",
9748
9870
  "addressValidation",
@@ -9787,47 +9909,8 @@ function useAddressValidation({
9787
9909
  };
9788
9910
  }
9789
9911
 
9790
- // src/hooks/use-supported-deposit-tokens.ts
9791
- import { useQuery as useQuery9 } from "@tanstack/react-query";
9792
- import {
9793
- getSupportedDepositTokens as getSupportedDepositTokens2
9794
- } from "@unifold/core";
9795
- function useSupportedDepositTokens(publishableKey, options) {
9796
- const hasDestination = options?.destination_token_address && options?.destination_chain_id && options?.destination_chain_type;
9797
- const filteredOptions = {
9798
- ...hasDestination ? {
9799
- destination_token_address: options.destination_token_address,
9800
- destination_chain_id: options.destination_chain_id,
9801
- destination_chain_type: options.destination_chain_type
9802
- } : {},
9803
- ...options?.product_type ? { product_type: options.product_type } : {}
9804
- };
9805
- const hasFilteredOptions = Object.keys(filteredOptions).length > 0;
9806
- return useQuery9({
9807
- queryKey: [
9808
- "unifold",
9809
- "supportedDepositTokens",
9810
- publishableKey,
9811
- filteredOptions?.destination_token_address ?? null,
9812
- filteredOptions?.destination_chain_id ?? null,
9813
- filteredOptions?.destination_chain_type ?? null,
9814
- filteredOptions?.product_type ?? null
9815
- ],
9816
- queryFn: () => getSupportedDepositTokens2(
9817
- publishableKey,
9818
- hasFilteredOptions ? filteredOptions : void 0
9819
- ),
9820
- staleTime: 1e3 * 60 * 5,
9821
- // 5 minutes — token list rarely changes
9822
- gcTime: 1e3 * 60 * 30,
9823
- // 30 minutes in cache
9824
- refetchOnMount: false,
9825
- refetchOnWindowFocus: false
9826
- });
9827
- }
9828
-
9829
9912
  // src/components/deposits/TransferCryptoSingleInput.tsx
9830
- import { useState as useState26, useEffect as useEffect21, useMemo as useMemo5 } from "react";
9913
+ import { useState as useState26, useEffect as useEffect21, useMemo as useMemo6 } from "react";
9831
9914
  import {
9832
9915
  ChevronDown as ChevronDown4,
9833
9916
  ChevronUp as ChevronUp3,
@@ -10101,7 +10184,7 @@ function DepositsModal({
10101
10184
  }
10102
10185
 
10103
10186
  // src/components/deposits/TokenSelectorSheet.tsx
10104
- import { useState as useState22, useMemo as useMemo4, useEffect as useEffect19 } from "react";
10187
+ import { useState as useState22, useMemo as useMemo5, useEffect as useEffect19 } from "react";
10105
10188
  import { ArrowLeft as ArrowLeft2, X as X4 } from "lucide-react";
10106
10189
  import { jsx as jsx41, jsxs as jsxs36 } from "react/jsx-runtime";
10107
10190
  var STORAGE_KEY = "unifold_recent_tokens";
@@ -10166,7 +10249,7 @@ function TokenSelectorSheet({
10166
10249
  useEffect19(() => {
10167
10250
  setRecentTokens(getRecentTokens());
10168
10251
  }, []);
10169
- const allOptions = useMemo4(() => {
10252
+ const allOptions = useMemo5(() => {
10170
10253
  const options = [];
10171
10254
  tokens.forEach((token) => {
10172
10255
  token.chains.forEach((chain) => {
@@ -10175,7 +10258,7 @@ function TokenSelectorSheet({
10175
10258
  });
10176
10259
  return options;
10177
10260
  }, [tokens]);
10178
- const quickSelectOptions = useMemo4(() => {
10261
+ const quickSelectOptions = useMemo5(() => {
10179
10262
  const result = [];
10180
10263
  const seen = /* @__PURE__ */ new Set();
10181
10264
  const addOption = (symbol, chainType, chainId, isRecent) => {
@@ -10207,7 +10290,7 @@ function TokenSelectorSheet({
10207
10290
  });
10208
10291
  setRecentTokens(updated);
10209
10292
  };
10210
- const filteredOptions = useMemo4(() => {
10293
+ const filteredOptions = useMemo5(() => {
10211
10294
  if (!searchQuery.trim()) return allOptions;
10212
10295
  const query = searchQuery.toLowerCase();
10213
10296
  return allOptions.filter(
@@ -10957,7 +11040,7 @@ import {
10957
11040
  } from "@unifold/core";
10958
11041
 
10959
11042
  // src/hooks/use-hypercore-activation.ts
10960
- import { useQuery as useQuery10 } from "@tanstack/react-query";
11043
+ import { useQuery as useQuery11 } from "@tanstack/react-query";
10961
11044
  import {
10962
11045
  checkHypercoreActivation
10963
11046
  } from "@unifold/core";
@@ -10978,7 +11061,7 @@ function useHypercoreActivation(params) {
10978
11061
  const recipient = recipientAddress?.trim() ?? "";
10979
11062
  const source = sourceAddress?.trim() ?? "";
10980
11063
  const hasAddresses = !!recipient && !!source;
10981
- const { data, isLoading } = useQuery10({
11064
+ const { data, isLoading } = useQuery11({
10982
11065
  queryKey: [
10983
11066
  "unifold",
10984
11067
  "hypercoreActivation",
@@ -11114,7 +11197,7 @@ function TransferCryptoSingleInput({
11114
11197
  const wallets = externalWallets?.length ? externalWallets : depositAddressResponse?.data ?? [];
11115
11198
  const loading = externalWallets?.length ? false : walletsLoading;
11116
11199
  const error = walletsError?.message ?? null;
11117
- const allAvailableChains = useMemo5(() => {
11200
+ const allAvailableChains = useMemo6(() => {
11118
11201
  const chainsMap = /* @__PURE__ */ new Map();
11119
11202
  supportedTokens.forEach((t12) => {
11120
11203
  t12.chains.forEach((c) => {
@@ -11584,7 +11667,7 @@ function TransferCryptoSingleInput({
11584
11667
  }
11585
11668
 
11586
11669
  // src/components/deposits/TransferCryptoDoubleInput.tsx
11587
- import { useState as useState27, useEffect as useEffect22, useMemo as useMemo6 } from "react";
11670
+ import { useState as useState27, useEffect as useEffect22, useMemo as useMemo7 } from "react";
11588
11671
  import {
11589
11672
  ChevronDown as ChevronDown6,
11590
11673
  ChevronUp as ChevronUp5,
@@ -11799,7 +11882,7 @@ function TransferCryptoDoubleInput({
11799
11882
  const wallets = externalWallets?.length ? externalWallets : depositAddressResponse?.data ?? [];
11800
11883
  const loading = externalWallets?.length ? false : walletsLoading;
11801
11884
  const error = walletsError?.message ?? null;
11802
- const allAvailableChains = useMemo6(() => {
11885
+ const allAvailableChains = useMemo7(() => {
11803
11886
  const chainsMap = /* @__PURE__ */ new Map();
11804
11887
  supportedTokens.forEach((t12) => {
11805
11888
  t12.chains.forEach((c) => {
@@ -12210,13 +12293,13 @@ function TransferCryptoDoubleInput({
12210
12293
  import * as React29 from "react";
12211
12294
  import {
12212
12295
  getAddressBalances as getAddressBalances2,
12213
- getSupportedDepositTokens as getSupportedDepositTokens3,
12296
+ getSupportedDepositTokens as getSupportedDepositTokens2,
12214
12297
  buildSolanaTransaction,
12215
12298
  sendSolanaTransaction as sendSolanaTransactionToBackend
12216
12299
  } from "@unifold/core";
12217
12300
 
12218
12301
  // src/hooks/use-deposit-quote.ts
12219
- import { useQuery as useQuery11 } from "@tanstack/react-query";
12302
+ import { useQuery as useQuery12 } from "@tanstack/react-query";
12220
12303
  import {
12221
12304
  getDepositQuote
12222
12305
  } from "@unifold/core";
@@ -12245,7 +12328,7 @@ function useDepositQuote(params) {
12245
12328
  ...adjustForSlippage ? { adjust_for_slippage: true } : {},
12246
12329
  ...stablecoinParity ? { stablecoin_parity: true } : {}
12247
12330
  };
12248
- return useQuery11({
12331
+ return useQuery12({
12249
12332
  queryKey: [
12250
12333
  "unifold",
12251
12334
  "depositQuote",
@@ -12364,7 +12447,7 @@ function SelectTokenView({
12364
12447
  ),
12365
12448
  walletInfoProp ? /* @__PURE__ */ jsx50("div", { className: "uf-flex uf-w-full uf-justify-center uf-mb-6", children: /* @__PURE__ */ jsx50(WalletWithNetworkBadge, { walletInfo: walletInfoProp }) }) : null,
12366
12449
  /* @__PURE__ */ jsxs43("div", { className: "uf-flex uf-min-h-0 uf-flex-1 uf-flex-col", children: [
12367
- /* @__PURE__ */ jsx50("div", { className: "uf-h-[220px] uf-shrink-0 uf-overflow-y-auto [scrollbar-width:none] [&::-webkit-scrollbar]:uf-hidden", children: /* @__PURE__ */ jsx50("div", { className: "uf-space-y-2", children: isLoading ? /* @__PURE__ */ jsx50("div", { className: "uf-flex uf-items-center uf-justify-center uf-py-12", children: /* @__PURE__ */ jsx50(
12450
+ /* @__PURE__ */ jsx50("div", { className: "uf-h-[300px] uf-shrink-0 uf-overflow-y-auto [scrollbar-width:none] [&::-webkit-scrollbar]:uf-hidden", children: /* @__PURE__ */ jsx50("div", { className: "uf-space-y-2", children: isLoading ? /* @__PURE__ */ jsx50("div", { className: "uf-flex uf-items-center uf-justify-center uf-py-12", children: /* @__PURE__ */ jsx50(
12368
12451
  Loader25,
12369
12452
  {
12370
12453
  className: "uf-w-6 uf-h-6 uf-animate-spin",
@@ -13499,7 +13582,7 @@ function BrowserWalletModal({
13499
13582
  destination_chain_type: depositWallet.destination_chain_type,
13500
13583
  ...productType ? { product_type: productType } : {}
13501
13584
  };
13502
- const response = await getSupportedDepositTokens3(
13585
+ const response = await getSupportedDepositTokens2(
13503
13586
  publishableKey,
13504
13587
  options
13505
13588
  );
@@ -14767,7 +14850,7 @@ function DepositModal({
14767
14850
  depositTrackerSubTitle = t7.depositTracker.subtitle
14768
14851
  }) {
14769
14852
  const { colors: colors2, fonts, components } = useTheme();
14770
- const effectiveInitialScreen = useMemo9(() => {
14853
+ const effectiveInitialScreen = useMemo10(() => {
14771
14854
  const s = initialScreen ?? "main";
14772
14855
  if (s === "tracker" && hideDepositTracker) return "main";
14773
14856
  if (s === "cashapp" && !enableCashApp) return "main";
@@ -14826,11 +14909,12 @@ function DepositModal({
14826
14909
  return sum + (isNaN(usd) ? 0 : usd);
14827
14910
  }, 0);
14828
14911
  const balanceUsd = totalUsd > 0 ? totalUsd.toLocaleString(void 0, { minimumFractionDigits: 2, maximumFractionDigits: 2 }) : null;
14829
- setConnectedExchange((prev) => ({ ...prev, balanceUsd, isLoading: false }));
14912
+ setConnectedExchange((prev) => prev ? { ...prev, balanceUsd, isLoading: false } : null);
14830
14913
  };
14831
14914
  getIntegrationHoldings2(IntegrationProvider2.COINBASE, stored.access_token, publishableKey).then(processHoldings).catch(async () => {
14832
14915
  try {
14833
14916
  const refreshResult = await refreshIntegrationToken2(stored.access_token, publishableKey);
14917
+ if (!getStoredIntegrationToken(IntegrationProvider2.COINBASE)) return;
14834
14918
  setStoredIntegrationToken({
14835
14919
  integration_provider: IntegrationProvider2.COINBASE,
14836
14920
  access_token: refreshResult.access_token,
@@ -14839,6 +14923,7 @@ function DepositModal({
14839
14923
  const retryResult = await getIntegrationHoldings2(IntegrationProvider2.COINBASE, refreshResult.access_token, publishableKey);
14840
14924
  processHoldings(retryResult);
14841
14925
  } catch {
14926
+ if (!getStoredIntegrationToken(IntegrationProvider2.COINBASE)) return;
14842
14927
  clearStoredIntegrationToken(IntegrationProvider2.COINBASE);
14843
14928
  setConnectedExchange(null);
14844
14929
  }
@@ -15006,6 +15091,14 @@ function DepositModal({
15006
15091
  setBrowserWalletInfo(null);
15007
15092
  setBrowserWalletModalOpen(false);
15008
15093
  };
15094
+ const handleExchangeDisconnect = () => {
15095
+ const stored = getStoredIntegrationToken(IntegrationProvider2.COINBASE);
15096
+ if (stored) {
15097
+ revokeIntegrationToken(stored.access_token, publishableKey);
15098
+ }
15099
+ clearStoredIntegrationToken(IntegrationProvider2.COINBASE);
15100
+ setConnectedExchange(null);
15101
+ };
15009
15102
  const handleClose = () => {
15010
15103
  onOpenChange(false);
15011
15104
  if (resetViewTimeoutRef.current) {
@@ -15211,6 +15304,7 @@ function DepositModal({
15211
15304
  setCoinbaseSkipToHoldings(true);
15212
15305
  setView("coinbase_connect");
15213
15306
  },
15307
+ onDisconnect: handleExchangeDisconnect,
15214
15308
  title: i18n.connectExchange.title,
15215
15309
  subtitle: i18n.connectExchange.subtitle,
15216
15310
  exchanges: integrationExchanges,
@@ -15415,34 +15509,37 @@ function DepositModal({
15415
15509
  ),
15416
15510
  depositPoweredByFooter
15417
15511
  ] })
15418
- ] }) : view === "coinbase_connect" ? /* @__PURE__ */ jsx56(
15419
- CoinbaseConnect,
15420
- {
15421
- publishableKey,
15422
- userId,
15423
- wallets,
15424
- recipientAddress,
15425
- destinationTokenAddress: destinationTokenAddress ?? "",
15426
- destinationChainId: destinationChainId ?? "",
15427
- destinationChainType: destinationChainType ?? "",
15428
- onTransferSuccess: (result) => {
15429
- onDepositSuccess?.({
15430
- message: "Transfer completed via Coinbase Connect",
15431
- transaction: result
15432
- });
15433
- },
15434
- onTransferError: (error) => {
15435
- onDepositError?.({
15436
- message: error.message,
15437
- error
15438
- });
15439
- },
15440
- onBack: handleBack,
15441
- onClose: handleClose,
15442
- skipToHoldings: coinbaseSkipToHoldings,
15443
- onExecutionsChange: setDepositExecutions
15444
- }
15445
- ) : view === "cashapp" ? /* @__PURE__ */ jsxs49(Fragment12, { children: [
15512
+ ] }) : view === "coinbase_connect" ? /* @__PURE__ */ jsxs49("div", { className: "uf-flex uf-flex-col uf-gap-1.5", children: [
15513
+ /* @__PURE__ */ jsx56(
15514
+ CoinbaseConnect,
15515
+ {
15516
+ publishableKey,
15517
+ userId,
15518
+ wallets,
15519
+ recipientAddress,
15520
+ destinationTokenAddress: destinationTokenAddress ?? "",
15521
+ destinationChainId: destinationChainId ?? "",
15522
+ destinationChainType: destinationChainType ?? "",
15523
+ onTransferSuccess: (result) => {
15524
+ onDepositSuccess?.({
15525
+ message: "Transfer completed via Coinbase Connect",
15526
+ transaction: result
15527
+ });
15528
+ },
15529
+ onTransferError: (error) => {
15530
+ onDepositError?.({
15531
+ message: error.message,
15532
+ error
15533
+ });
15534
+ },
15535
+ onBack: handleBack,
15536
+ onClose: handleClose,
15537
+ skipToHoldings: coinbaseSkipToHoldings,
15538
+ onExecutionsChange: setDepositExecutions
15539
+ }
15540
+ ),
15541
+ depositPoweredByFooter
15542
+ ] }) : view === "cashapp" ? /* @__PURE__ */ jsxs49(Fragment12, { children: [
15446
15543
  /* @__PURE__ */ jsx56(
15447
15544
  DepositHeader,
15448
15545
  {
@@ -15530,12 +15627,12 @@ import {
15530
15627
  useLayoutEffect as useLayoutEffect3,
15531
15628
  useCallback as useCallback7,
15532
15629
  useRef as useRef9,
15533
- useMemo as useMemo10
15630
+ useMemo as useMemo11
15534
15631
  } from "react";
15535
15632
  import { AlertTriangle as AlertTriangle3, ChevronRight as ChevronRight15 } from "lucide-react";
15536
15633
 
15537
15634
  // src/hooks/use-payment-intent.ts
15538
- import { useQuery as useQuery12 } from "@tanstack/react-query";
15635
+ import { useQuery as useQuery13 } from "@tanstack/react-query";
15539
15636
  import { retrievePaymentIntent } from "@unifold/core";
15540
15637
  var TERMINAL_STATUSES = /* @__PURE__ */ new Set([
15541
15638
  "succeeded",
@@ -15550,7 +15647,7 @@ function usePaymentIntent(params) {
15550
15647
  enabled = true,
15551
15648
  pollingInterval = 3e3
15552
15649
  } = params;
15553
- return useQuery12({
15650
+ return useQuery13({
15554
15651
  queryKey: ["unifold", "paymentIntent", clientSecret, publishableKey],
15555
15652
  queryFn: () => retrievePaymentIntent(clientSecret, publishableKey),
15556
15653
  enabled: enabled && !!clientSecret && !!publishableKey,
@@ -15665,14 +15762,14 @@ function CheckoutModal({
15665
15762
  });
15666
15763
  }
15667
15764
  }, [paymentIntent, onCheckoutSuccess, browserWalletModalOpen]);
15668
- const wallets = useMemo10(() => {
15765
+ const wallets = useMemo11(() => {
15669
15766
  if (!paymentIntent) return [];
15670
15767
  return mapDepositAddressesToWallets(
15671
15768
  paymentIntent.deposit_addresses,
15672
15769
  paymentIntent
15673
15770
  );
15674
15771
  }, [paymentIntent]);
15675
- const formatCryptoAmount = useMemo10(() => {
15772
+ const formatCryptoAmount = useMemo11(() => {
15676
15773
  if (!paymentIntent) return (_) => "";
15677
15774
  const decimals = paymentIntent.destination_token_decimals ?? 6;
15678
15775
  const symbol = paymentIntent.currency.toUpperCase();
@@ -15682,7 +15779,7 @@ function CheckoutModal({
15682
15779
  return `${formatted} ${symbol}`;
15683
15780
  };
15684
15781
  }, [paymentIntent]);
15685
- const remainingAmountUsd = useMemo10(() => {
15782
+ const remainingAmountUsd = useMemo11(() => {
15686
15783
  if (!paymentIntent) return void 0;
15687
15784
  const total = parseFloat(paymentIntent.destination_amount_usd || paymentIntent.amount_usd);
15688
15785
  const received = parseFloat(paymentIntent.destination_amount_received_usd || paymentIntent.amount_received_usd);
@@ -15690,7 +15787,7 @@ function CheckoutModal({
15690
15787
  const remaining = total - received;
15691
15788
  return remaining > 0 ? remaining.toFixed(2) : "0.00";
15692
15789
  }, [paymentIntent]);
15693
- const remainingCrypto = useMemo10(() => {
15790
+ const remainingCrypto = useMemo11(() => {
15694
15791
  if (!paymentIntent) return void 0;
15695
15792
  const total = BigInt(paymentIntent.destination_amount || paymentIntent.amount);
15696
15793
  const received = BigInt(paymentIntent.destination_amount_received || paymentIntent.amount_received);
@@ -15698,7 +15795,7 @@ function CheckoutModal({
15698
15795
  return remaining > 0n ? remaining.toString() : "0";
15699
15796
  }, [paymentIntent]);
15700
15797
  const [selectedSource, setSelectedSource] = useState32(null);
15701
- const remainingDestinationAmount = useMemo10(() => {
15798
+ const remainingDestinationAmount = useMemo11(() => {
15702
15799
  if (!paymentIntent) return "0";
15703
15800
  const remaining = BigInt(paymentIntent.destination_amount) - BigInt(paymentIntent.destination_amount_received);
15704
15801
  return remaining > 0n ? remaining.toString() : "0";
@@ -15716,7 +15813,7 @@ function CheckoutModal({
15716
15813
  stablecoinParity: paymentIntent?.stablecoin_parity ?? false,
15717
15814
  enabled: open && view === "transfer" && !!paymentIntent && !!selectedSource && remainingDestinationAmount !== "0"
15718
15815
  });
15719
- const effectiveCheckoutQuote = useMemo10(() => {
15816
+ const effectiveCheckoutQuote = useMemo11(() => {
15720
15817
  if (!sourceQuote || !selectedSource) return null;
15721
15818
  const baseQuote = {
15722
15819
  sourceAmount: sourceQuote.source_amount,
@@ -16213,12 +16310,12 @@ import {
16213
16310
  import { AlertTriangle as AlertTriangle5, ChevronRight as ChevronRight17, Clock as Clock6 } from "lucide-react";
16214
16311
 
16215
16312
  // src/hooks/use-supported-destination-tokens.ts
16216
- import { useQuery as useQuery13 } from "@tanstack/react-query";
16313
+ import { useQuery as useQuery14 } from "@tanstack/react-query";
16217
16314
  import {
16218
16315
  getSupportedDestinationTokens
16219
16316
  } from "@unifold/core";
16220
16317
  function useSupportedDestinationTokens(publishableKey, enabled = true) {
16221
- return useQuery13({
16318
+ return useQuery14({
16222
16319
  queryKey: ["unifold", "supportedDestinationTokens", publishableKey],
16223
16320
  queryFn: () => getSupportedDestinationTokens(publishableKey),
16224
16321
  staleTime: 1e3 * 60 * 5,
@@ -16247,8 +16344,8 @@ function useDefaultDestinationToken({
16247
16344
  }
16248
16345
 
16249
16346
  // src/hooks/use-source-token-validation.ts
16250
- import { useQuery as useQuery14 } from "@tanstack/react-query";
16251
- import { getSupportedDepositTokens as getSupportedDepositTokens4 } from "@unifold/core";
16347
+ import { useQuery as useQuery15 } from "@tanstack/react-query";
16348
+ import { getSupportedDepositTokens as getSupportedDepositTokens3 } from "@unifold/core";
16252
16349
  function useSourceTokenValidation(params) {
16253
16350
  const {
16254
16351
  sourceChainType,
@@ -16259,7 +16356,7 @@ function useSourceTokenValidation(params) {
16259
16356
  enabled = true
16260
16357
  } = params;
16261
16358
  const hasParams = !!sourceChainType && !!sourceChainId && !!sourceTokenAddress;
16262
- return useQuery14({
16359
+ return useQuery15({
16263
16360
  queryKey: [
16264
16361
  "unifold",
16265
16362
  "sourceTokenValidation",
@@ -16269,7 +16366,7 @@ function useSourceTokenValidation(params) {
16269
16366
  publishableKey
16270
16367
  ],
16271
16368
  queryFn: async () => {
16272
- const res = await getSupportedDepositTokens4(publishableKey);
16369
+ const res = await getSupportedDepositTokens3(publishableKey);
16273
16370
  let matchedMinUsd = null;
16274
16371
  let matchedProcessingTime = null;
16275
16372
  let matchedSlippage = null;
@@ -16307,7 +16404,7 @@ function useSourceTokenValidation(params) {
16307
16404
  }
16308
16405
 
16309
16406
  // src/hooks/use-address-balance.ts
16310
- import { useQuery as useQuery15 } from "@tanstack/react-query";
16407
+ import { useQuery as useQuery16 } from "@tanstack/react-query";
16311
16408
  import { getAddressBalance as getAddressBalance2 } from "@unifold/core";
16312
16409
  function useAddressBalance(params) {
16313
16410
  const {
@@ -16319,7 +16416,7 @@ function useAddressBalance(params) {
16319
16416
  enabled = true
16320
16417
  } = params;
16321
16418
  const hasParams = !!address && !!chainType && !!chainId && !!tokenAddress;
16322
- return useQuery15({
16419
+ return useQuery16({
16323
16420
  queryKey: [
16324
16421
  "unifold",
16325
16422
  "addressBalance",
@@ -16368,11 +16465,11 @@ function useAddressBalance(params) {
16368
16465
  }
16369
16466
 
16370
16467
  // src/hooks/use-executions.ts
16371
- import { useQuery as useQuery16 } from "@tanstack/react-query";
16468
+ import { useQuery as useQuery17 } from "@tanstack/react-query";
16372
16469
  import { queryExecutions as queryExecutions4, ActionType as ActionType4 } from "@unifold/core";
16373
16470
  function useExecutions(userId, publishableKey, options) {
16374
16471
  const actionType = options?.actionType ?? ActionType4.Deposit;
16375
- return useQuery16({
16472
+ return useQuery17({
16376
16473
  queryKey: ["unifold", "executions", actionType, userId, publishableKey],
16377
16474
  queryFn: () => queryExecutions4(userId, publishableKey, actionType),
16378
16475
  enabled: (options?.enabled ?? true) && !!userId,
@@ -16658,7 +16755,7 @@ function WithdrawDoubleInput({
16658
16755
  }
16659
16756
 
16660
16757
  // src/components/withdrawals/WithdrawForm.tsx
16661
- import { useState as useState34, useCallback as useCallback8, useMemo as useMemo12, useEffect as useEffect29 } from "react";
16758
+ import { useState as useState34, useCallback as useCallback8, useMemo as useMemo13, useEffect as useEffect29 } from "react";
16662
16759
  import {
16663
16760
  AlertTriangle as AlertTriangle4,
16664
16761
  ArrowUpDown,
@@ -16676,7 +16773,7 @@ import {
16676
16773
  } from "@unifold/core";
16677
16774
 
16678
16775
  // src/hooks/use-verify-recipient-address.ts
16679
- import { useQuery as useQuery17 } from "@tanstack/react-query";
16776
+ import { useQuery as useQuery18 } from "@tanstack/react-query";
16680
16777
  import { verifyRecipientAddress as verifyRecipientAddress2 } from "@unifold/core";
16681
16778
  function useVerifyRecipientAddress(params) {
16682
16779
  const {
@@ -16689,7 +16786,7 @@ function useVerifyRecipientAddress(params) {
16689
16786
  } = params;
16690
16787
  const trimmedAddress = recipientAddress?.trim() || "";
16691
16788
  const hasAllParams = !!chainType && !!chainId && !!tokenAddress && trimmedAddress.length > 0;
16692
- return useQuery17({
16789
+ return useQuery18({
16693
16790
  queryKey: [
16694
16791
  "unifold",
16695
16792
  "verifyRecipientAddress",
@@ -16968,11 +17065,11 @@ async function detectBrowserWallet(chainType, senderAddress) {
16968
17065
  }
16969
17066
 
16970
17067
  // src/hooks/use-hypercore-withdraw-activation.ts
16971
- import { useMemo as useMemo11 } from "react";
17068
+ import { useMemo as useMemo12 } from "react";
16972
17069
  import { ActionType as ActionType6 } from "@unifold/core";
16973
17070
 
16974
17071
  // src/hooks/use-get-deposit-address.ts
16975
- import { useQuery as useQuery18 } from "@tanstack/react-query";
17072
+ import { useQuery as useQuery19 } from "@tanstack/react-query";
16976
17073
  import {
16977
17074
  getDepositAddress
16978
17075
  } from "@unifold/core";
@@ -16988,7 +17085,7 @@ function useGetDepositAddress(params) {
16988
17085
  enabled = true
16989
17086
  } = params;
16990
17087
  const canFire = !!userId && !!recipientAddress && !!destinationChainType && !!destinationChainId && !!destinationTokenAddress;
16991
- return useQuery18({
17088
+ return useQuery19({
16992
17089
  queryKey: [
16993
17090
  "unifold",
16994
17091
  "getDepositAddress",
@@ -17057,7 +17154,7 @@ function useHypercoreWithdrawActivation(params) {
17057
17154
  actionType: ActionType6.Withdraw,
17058
17155
  enabled: enabled && isHypercore(sourceChainId)
17059
17156
  });
17060
- const depositWalletAddress = useMemo11(() => {
17157
+ const depositWalletAddress = useMemo12(() => {
17061
17158
  const wallets = depositWalletLookup.data?.data ?? [];
17062
17159
  return wallets.find((w) => w.chain_type === sourceChainType)?.address;
17063
17160
  }, [depositWalletLookup.data, sourceChainType]);
@@ -17183,7 +17280,7 @@ function WithdrawForm({
17183
17280
  enabled: debouncedAddress.length > 5 && !!selectedChain
17184
17281
  });
17185
17282
  const isDebouncing = trimmedAddress !== debouncedAddress;
17186
- const addressError = useMemo12(() => {
17283
+ const addressError = useMemo13(() => {
17187
17284
  if (!trimmedAddress || trimmedAddress.length <= 5) return null;
17188
17285
  if (isDebouncing || isVerifyingAddress) return null;
17189
17286
  if (verifyError) return t9.invalidAddress;
@@ -17209,33 +17306,33 @@ function WithdrawForm({
17209
17306
  destinationTokenAddress: selectedChain?.token_address,
17210
17307
  enabled: isAddressValid
17211
17308
  });
17212
- const exchangeRate = useMemo12(() => {
17309
+ const exchangeRate = useMemo13(() => {
17213
17310
  if (!balanceData?.exchangeRate) return 0;
17214
17311
  return parseFloat(balanceData.exchangeRate);
17215
17312
  }, [balanceData]);
17216
- const balanceCrypto = useMemo12(() => {
17313
+ const balanceCrypto = useMemo13(() => {
17217
17314
  if (!balanceData?.balanceHuman) return 0;
17218
17315
  return parseFloat(balanceData.balanceHuman);
17219
17316
  }, [balanceData]);
17220
- const balanceUsdNum = useMemo12(() => {
17317
+ const balanceUsdNum = useMemo13(() => {
17221
17318
  if (!balanceData?.balanceUsd) return 0;
17222
17319
  return parseFloat(balanceData.balanceUsd);
17223
17320
  }, [balanceData]);
17224
17321
  const tokenSymbol = sourceTokenSymbol || balanceData?.symbol || "TOKEN";
17225
17322
  const sourceDecimals = balanceData?.decimals ?? 6;
17226
- const cryptoAmountFromInput = useMemo12(() => {
17323
+ const cryptoAmountFromInput = useMemo13(() => {
17227
17324
  const val = parseFloat(amount);
17228
17325
  if (!val || val <= 0) return 0;
17229
17326
  if (inputUnit === "crypto") return val;
17230
17327
  return exchangeRate > 0 ? val / exchangeRate : 0;
17231
17328
  }, [amount, inputUnit, exchangeRate]);
17232
- const fiatAmountFromInput = useMemo12(() => {
17329
+ const fiatAmountFromInput = useMemo13(() => {
17233
17330
  const val = parseFloat(amount);
17234
17331
  if (!val || val <= 0) return 0;
17235
17332
  if (inputUnit === "fiat") return val;
17236
17333
  return val * exchangeRate;
17237
17334
  }, [amount, inputUnit, exchangeRate]);
17238
- const convertedDisplay = useMemo12(() => {
17335
+ const convertedDisplay = useMemo13(() => {
17239
17336
  if (!amount || parseFloat(amount) <= 0) return null;
17240
17337
  if (inputUnit === "crypto") {
17241
17338
  return `$${fiatAmountFromInput.toLocaleString(void 0, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`;
@@ -17244,7 +17341,7 @@ function WithdrawForm({
17244
17341
  const displayDecimals = isStablecoin ? 2 : 6;
17245
17342
  return `${cryptoDisplay.toLocaleString(void 0, { minimumFractionDigits: 2, maximumFractionDigits: displayDecimals })} ${tokenSymbol}`;
17246
17343
  }, [amount, inputUnit, fiatAmountFromInput, cryptoAmountFromInput, balanceCrypto, balanceData, tokenSymbol, isMaxed, isStablecoin]);
17247
- const balanceDisplay = useMemo12(() => {
17344
+ const balanceDisplay = useMemo13(() => {
17248
17345
  if (isLoadingBalance || !balanceData) return null;
17249
17346
  if (inputUnit === "crypto") {
17250
17347
  const displayDecimals = isStablecoin ? 2 : 6;
@@ -18226,7 +18323,7 @@ function WithdrawModal({
18226
18323
  }
18227
18324
 
18228
18325
  // src/components/withdrawals/WithdrawTokenSelector.tsx
18229
- import { useState as useState37, useMemo as useMemo13 } from "react";
18326
+ import { useState as useState37, useMemo as useMemo14 } from "react";
18230
18327
  import { Search } from "lucide-react";
18231
18328
  import { jsx as jsx63, jsxs as jsxs56 } from "react/jsx-runtime";
18232
18329
  var t11 = i18n.withdrawModal;
@@ -18238,7 +18335,7 @@ function WithdrawTokenSelector({
18238
18335
  const { themeClass, colors: colors2, fonts, components } = useTheme();
18239
18336
  const [searchQuery, setSearchQuery] = useState37("");
18240
18337
  const [hoveredKey, setHoveredKey] = useState37(null);
18241
- const allOptions = useMemo13(() => {
18338
+ const allOptions = useMemo14(() => {
18242
18339
  const options = [];
18243
18340
  tokens.forEach((token) => {
18244
18341
  token.chains.forEach((chain) => {
@@ -18247,7 +18344,7 @@ function WithdrawTokenSelector({
18247
18344
  });
18248
18345
  return options;
18249
18346
  }, [tokens]);
18250
- const filteredOptions = useMemo13(() => {
18347
+ const filteredOptions = useMemo14(() => {
18251
18348
  if (!searchQuery.trim()) return allOptions;
18252
18349
  const query = searchQuery.toLowerCase();
18253
18350
  return allOptions.filter(