@unifold/ui-react 0.1.49 → 0.1.51

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.d.mts CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import { AutoSwapResponse, ChainType, DepositEvent, Wallet, FiatCurrency, ExecutionStatus, FeaturedToken, PaymentNetwork, DestinationToken, DestinationTokenChain, PaymentIntent, DepositQuote, SupportedDestinationTokensResponse, VerifyAddressResponse } from '@unifold/core';
2
+ import { AutoSwapResponse, ChainType, DepositEvent, Wallet, FiatCurrency, ExecutionStatus, FeaturedToken, PaymentNetwork, DestinationToken, DestinationTokenChain, PaymentIntent, DepositQuote, SupportedDestinationTokensResponse, SupportedDepositTokensResponse, VerifyAddressResponse } from '@unifold/core';
3
3
  export { ChainType } from '@unifold/core';
4
4
  import * as _tanstack_react_query from '@tanstack/react-query';
5
5
  import * as class_variance_authority_types from 'class-variance-authority/types';
@@ -773,12 +773,14 @@ declare const TooltipContent: React$1.ForwardRefExoticComponent<Omit<TooltipPrim
773
773
  * Result of the useAllowedCountry hook
774
774
  */
775
775
  interface AllowedCountryResult {
776
- /** Whether the user is in an allowed country. null while loading. */
776
+ /** Whether the user is in an allowed country/subdivision. null while loading. */
777
777
  isAllowed: boolean | null;
778
778
  /** ISO 3166-1 alpha-2 country code in lowercase (e.g., "us", "pt") */
779
779
  alpha2: string | null;
780
780
  /** Full country name (e.g., "United States", "Portugal") */
781
781
  country: string | null;
782
+ /** ISO 3166-2 subdivision code in lowercase (e.g., "ny", "43"), null if unavailable */
783
+ subdivisionCode: string | null;
782
784
  /** Whether the hook is still loading data */
783
785
  isLoading: boolean;
784
786
  /** Error if either API call failed */
@@ -820,7 +822,10 @@ interface UsePaymentIntentParams {
820
822
  * Hook to retrieve and poll a payment intent via react-query.
821
823
  *
822
824
  * Fetches the payment intent on mount and polls at the configured interval
823
- * so `amount_received_usd` and `status` stay up-to-date in the UI.
825
+ * so `amount_received_usd` and `status` stay up-to-date in the UI. Polling
826
+ * automatically stops once the PI reaches a terminal status. Locked-quote
827
+ * states like `awaiting_refund` / `refunding` / `refund_failed` keep polling
828
+ * because they can still flip.
824
829
  */
825
830
  declare function usePaymentIntent(params: UsePaymentIntentParams): _tanstack_react_query.UseQueryResult<PaymentIntent, Error>;
826
831
 
@@ -875,6 +880,22 @@ declare function useWithdrawPolling({ userId, publishableKey, depositWalletId, e
875
880
  */
876
881
  declare function useSupportedDestinationTokens(publishableKey: string, enabled?: boolean): _tanstack_react_query.UseQueryResult<SupportedDestinationTokensResponse, Error>;
877
882
 
883
+ interface UseSupportedDepositTokensOptions {
884
+ destination_token_address?: string;
885
+ destination_chain_id?: string;
886
+ destination_chain_type?: string;
887
+ }
888
+ /**
889
+ * Hook to fetch supported deposit tokens with caching and deduplication via react-query.
890
+ *
891
+ * Replaces manual useEffect + useState fetching with automatic caching (5 min stale),
892
+ * request deduplication across components, and built-in loading/error states.
893
+ *
894
+ * @param publishableKey - Publishable key for API calls
895
+ * @param options - Optional destination filters (all three must be provided to filter)
896
+ */
897
+ declare function useSupportedDepositTokens(publishableKey: string, options?: UseSupportedDepositTokensOptions): _tanstack_react_query.UseQueryResult<SupportedDepositTokensResponse, Error>;
898
+
878
899
  interface UseVerifyRecipientAddressParams {
879
900
  chainType?: string;
880
901
  chainId?: string;
@@ -1187,4 +1208,4 @@ declare function cn(...inputs: ClassValue[]): string;
1187
1208
  */
1188
1209
  declare function truncateAddress(address: string, startChars?: number, endChars?: number): string;
1189
1210
 
1190
- export { type AllowedCountryResult, type BrowserWalletAmountQuickSelect, Button, type ButtonProps, type ButtonTokens, BuyWithCard, type BuyWithCardProps, type CardTokens, CheckoutModal, type CheckoutModalProps, type ComponentConfig, type ComponentOverrides, type ComponentTokens, ConfirmingView, type ContainerTokens, CurrencyListItem, CurrencyListSection, CurrencyModal, type CustomThemeColors, type DepositConfirmationMode, DepositDetailContent, DepositExecutionItem, DepositHeader, DepositModal, type DepositModalInitialScreen, type DepositModalProps, DepositPollingUi, DepositSuccessToast, DepositTrackerButton, DepositWithCardButton, DepositsModal, type DetectedWallet, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, type EvmWalletProvider, type FontConfig, HYPERCORE_CHAIN_ID, type HeaderTokens, type InputTokens, type ListTokens, type ResolvedFonts, type SearchTokens, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, type SolanaWalletProvider, StyledQRCode, type ThemeColors, type ThemeConfig, type ThemeMode, ThemeProvider, type ThemeProviderProps, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, TransferCryptoButton, TransferCryptoDoubleInput, TransferCryptoSingleInput, type UseDepositQuoteParams, type UsePaymentIntentParams, WithdrawConfirmingView, WithdrawDoubleInput, WithdrawExecutionItem, WithdrawForm, WithdrawModal, type WithdrawModalProps, WithdrawTokenSelector, type WithdrawTransactionInfo, buttonVariants, cn, colors, defaultColors, detectBrowserWallet, getColors, isHypercoreChain, mergeColors, resolveComponentTokens, sendEvmWithdraw, sendHypercoreWithdraw, sendSolanaWithdraw, truncateAddress, useAddressBalance, useAllowedCountry, useDepositPolling, useDepositQuote, usePaymentIntent, useSourceTokenValidation, useSupportedDestinationTokens, useTheme, useVerifyRecipientAddress, useWithdrawPolling };
1211
+ export { type AllowedCountryResult, type BrowserWalletAmountQuickSelect, Button, type ButtonProps, type ButtonTokens, BuyWithCard, type BuyWithCardProps, type CardTokens, CheckoutModal, type CheckoutModalProps, type ComponentConfig, type ComponentOverrides, type ComponentTokens, ConfirmingView, type ContainerTokens, CurrencyListItem, CurrencyListSection, CurrencyModal, type CustomThemeColors, type DepositConfirmationMode, DepositDetailContent, DepositExecutionItem, DepositHeader, DepositModal, type DepositModalInitialScreen, type DepositModalProps, DepositPollingUi, DepositSuccessToast, DepositTrackerButton, DepositWithCardButton, DepositsModal, type DetectedWallet, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, type EvmWalletProvider, type FontConfig, HYPERCORE_CHAIN_ID, type HeaderTokens, type InputTokens, type ListTokens, type ResolvedFonts, type SearchTokens, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, type SolanaWalletProvider, StyledQRCode, type ThemeColors, type ThemeConfig, type ThemeMode, ThemeProvider, type ThemeProviderProps, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, TransferCryptoButton, TransferCryptoDoubleInput, TransferCryptoSingleInput, type UseDepositQuoteParams, type UsePaymentIntentParams, type UseSupportedDepositTokensOptions, WithdrawConfirmingView, WithdrawDoubleInput, WithdrawExecutionItem, WithdrawForm, WithdrawModal, type WithdrawModalProps, WithdrawTokenSelector, type WithdrawTransactionInfo, buttonVariants, cn, colors, defaultColors, detectBrowserWallet, getColors, isHypercoreChain, mergeColors, resolveComponentTokens, sendEvmWithdraw, sendHypercoreWithdraw, sendSolanaWithdraw, truncateAddress, useAddressBalance, useAllowedCountry, useDepositPolling, useDepositQuote, usePaymentIntent, useSourceTokenValidation, useSupportedDepositTokens, useSupportedDestinationTokens, useTheme, useVerifyRecipientAddress, useWithdrawPolling };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import { AutoSwapResponse, ChainType, DepositEvent, Wallet, FiatCurrency, ExecutionStatus, FeaturedToken, PaymentNetwork, DestinationToken, DestinationTokenChain, PaymentIntent, DepositQuote, SupportedDestinationTokensResponse, VerifyAddressResponse } from '@unifold/core';
2
+ import { AutoSwapResponse, ChainType, DepositEvent, Wallet, FiatCurrency, ExecutionStatus, FeaturedToken, PaymentNetwork, DestinationToken, DestinationTokenChain, PaymentIntent, DepositQuote, SupportedDestinationTokensResponse, SupportedDepositTokensResponse, VerifyAddressResponse } from '@unifold/core';
3
3
  export { ChainType } from '@unifold/core';
4
4
  import * as _tanstack_react_query from '@tanstack/react-query';
5
5
  import * as class_variance_authority_types from 'class-variance-authority/types';
@@ -773,12 +773,14 @@ declare const TooltipContent: React$1.ForwardRefExoticComponent<Omit<TooltipPrim
773
773
  * Result of the useAllowedCountry hook
774
774
  */
775
775
  interface AllowedCountryResult {
776
- /** Whether the user is in an allowed country. null while loading. */
776
+ /** Whether the user is in an allowed country/subdivision. null while loading. */
777
777
  isAllowed: boolean | null;
778
778
  /** ISO 3166-1 alpha-2 country code in lowercase (e.g., "us", "pt") */
779
779
  alpha2: string | null;
780
780
  /** Full country name (e.g., "United States", "Portugal") */
781
781
  country: string | null;
782
+ /** ISO 3166-2 subdivision code in lowercase (e.g., "ny", "43"), null if unavailable */
783
+ subdivisionCode: string | null;
782
784
  /** Whether the hook is still loading data */
783
785
  isLoading: boolean;
784
786
  /** Error if either API call failed */
@@ -820,7 +822,10 @@ interface UsePaymentIntentParams {
820
822
  * Hook to retrieve and poll a payment intent via react-query.
821
823
  *
822
824
  * Fetches the payment intent on mount and polls at the configured interval
823
- * so `amount_received_usd` and `status` stay up-to-date in the UI.
825
+ * so `amount_received_usd` and `status` stay up-to-date in the UI. Polling
826
+ * automatically stops once the PI reaches a terminal status. Locked-quote
827
+ * states like `awaiting_refund` / `refunding` / `refund_failed` keep polling
828
+ * because they can still flip.
824
829
  */
825
830
  declare function usePaymentIntent(params: UsePaymentIntentParams): _tanstack_react_query.UseQueryResult<PaymentIntent, Error>;
826
831
 
@@ -875,6 +880,22 @@ declare function useWithdrawPolling({ userId, publishableKey, depositWalletId, e
875
880
  */
876
881
  declare function useSupportedDestinationTokens(publishableKey: string, enabled?: boolean): _tanstack_react_query.UseQueryResult<SupportedDestinationTokensResponse, Error>;
877
882
 
883
+ interface UseSupportedDepositTokensOptions {
884
+ destination_token_address?: string;
885
+ destination_chain_id?: string;
886
+ destination_chain_type?: string;
887
+ }
888
+ /**
889
+ * Hook to fetch supported deposit tokens with caching and deduplication via react-query.
890
+ *
891
+ * Replaces manual useEffect + useState fetching with automatic caching (5 min stale),
892
+ * request deduplication across components, and built-in loading/error states.
893
+ *
894
+ * @param publishableKey - Publishable key for API calls
895
+ * @param options - Optional destination filters (all three must be provided to filter)
896
+ */
897
+ declare function useSupportedDepositTokens(publishableKey: string, options?: UseSupportedDepositTokensOptions): _tanstack_react_query.UseQueryResult<SupportedDepositTokensResponse, Error>;
898
+
878
899
  interface UseVerifyRecipientAddressParams {
879
900
  chainType?: string;
880
901
  chainId?: string;
@@ -1187,4 +1208,4 @@ declare function cn(...inputs: ClassValue[]): string;
1187
1208
  */
1188
1209
  declare function truncateAddress(address: string, startChars?: number, endChars?: number): string;
1189
1210
 
1190
- export { type AllowedCountryResult, type BrowserWalletAmountQuickSelect, Button, type ButtonProps, type ButtonTokens, BuyWithCard, type BuyWithCardProps, type CardTokens, CheckoutModal, type CheckoutModalProps, type ComponentConfig, type ComponentOverrides, type ComponentTokens, ConfirmingView, type ContainerTokens, CurrencyListItem, CurrencyListSection, CurrencyModal, type CustomThemeColors, type DepositConfirmationMode, DepositDetailContent, DepositExecutionItem, DepositHeader, DepositModal, type DepositModalInitialScreen, type DepositModalProps, DepositPollingUi, DepositSuccessToast, DepositTrackerButton, DepositWithCardButton, DepositsModal, type DetectedWallet, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, type EvmWalletProvider, type FontConfig, HYPERCORE_CHAIN_ID, type HeaderTokens, type InputTokens, type ListTokens, type ResolvedFonts, type SearchTokens, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, type SolanaWalletProvider, StyledQRCode, type ThemeColors, type ThemeConfig, type ThemeMode, ThemeProvider, type ThemeProviderProps, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, TransferCryptoButton, TransferCryptoDoubleInput, TransferCryptoSingleInput, type UseDepositQuoteParams, type UsePaymentIntentParams, WithdrawConfirmingView, WithdrawDoubleInput, WithdrawExecutionItem, WithdrawForm, WithdrawModal, type WithdrawModalProps, WithdrawTokenSelector, type WithdrawTransactionInfo, buttonVariants, cn, colors, defaultColors, detectBrowserWallet, getColors, isHypercoreChain, mergeColors, resolveComponentTokens, sendEvmWithdraw, sendHypercoreWithdraw, sendSolanaWithdraw, truncateAddress, useAddressBalance, useAllowedCountry, useDepositPolling, useDepositQuote, usePaymentIntent, useSourceTokenValidation, useSupportedDestinationTokens, useTheme, useVerifyRecipientAddress, useWithdrawPolling };
1211
+ export { type AllowedCountryResult, type BrowserWalletAmountQuickSelect, Button, type ButtonProps, type ButtonTokens, BuyWithCard, type BuyWithCardProps, type CardTokens, CheckoutModal, type CheckoutModalProps, type ComponentConfig, type ComponentOverrides, type ComponentTokens, ConfirmingView, type ContainerTokens, CurrencyListItem, CurrencyListSection, CurrencyModal, type CustomThemeColors, type DepositConfirmationMode, DepositDetailContent, DepositExecutionItem, DepositHeader, DepositModal, type DepositModalInitialScreen, type DepositModalProps, DepositPollingUi, DepositSuccessToast, DepositTrackerButton, DepositWithCardButton, DepositsModal, type DetectedWallet, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, type EvmWalletProvider, type FontConfig, HYPERCORE_CHAIN_ID, type HeaderTokens, type InputTokens, type ListTokens, type ResolvedFonts, type SearchTokens, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, type SolanaWalletProvider, StyledQRCode, type ThemeColors, type ThemeConfig, type ThemeMode, ThemeProvider, type ThemeProviderProps, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, TransferCryptoButton, TransferCryptoDoubleInput, TransferCryptoSingleInput, type UseDepositQuoteParams, type UsePaymentIntentParams, type UseSupportedDepositTokensOptions, WithdrawConfirmingView, WithdrawDoubleInput, WithdrawExecutionItem, WithdrawForm, WithdrawModal, type WithdrawModalProps, WithdrawTokenSelector, type WithdrawTransactionInfo, buttonVariants, cn, colors, defaultColors, detectBrowserWallet, getColors, isHypercoreChain, mergeColors, resolveComponentTokens, sendEvmWithdraw, sendHypercoreWithdraw, sendSolanaWithdraw, truncateAddress, useAddressBalance, useAllowedCountry, useDepositPolling, useDepositQuote, usePaymentIntent, useSourceTokenValidation, useSupportedDepositTokens, useSupportedDestinationTokens, useTheme, useVerifyRecipientAddress, useWithdrawPolling };
package/dist/index.js CHANGED
@@ -101,6 +101,7 @@ __export(index_exports, {
101
101
  useDepositQuote: () => useDepositQuote,
102
102
  usePaymentIntent: () => usePaymentIntent,
103
103
  useSourceTokenValidation: () => useSourceTokenValidation,
104
+ useSupportedDepositTokens: () => useSupportedDepositTokens,
104
105
  useSupportedDestinationTokens: () => useSupportedDestinationTokens,
105
106
  useTheme: () => useTheme,
106
107
  useVerifyRecipientAddress: () => useVerifyRecipientAddress,
@@ -1326,11 +1327,13 @@ function useUserIp() {
1326
1327
  queryKey: ["unifold", "userIpInfo"],
1327
1328
  queryFn: async () => {
1328
1329
  const data = await (0, import_core5.getIpAddress)();
1330
+ const subdivision = (data.subdivision_code || data.state || "").toLowerCase() || null;
1329
1331
  return {
1330
1332
  alpha2: data.alpha2.toLowerCase(),
1331
1333
  alpha3: data.alpha3?.toLowerCase(),
1332
1334
  country: data.country,
1333
- state: data.state?.toLowerCase() ?? null,
1335
+ state: subdivision,
1336
+ subdivisionCode: subdivision,
1334
1337
  ipAddress: data.ip_address
1335
1338
  };
1336
1339
  },
@@ -2667,7 +2670,7 @@ function BuyWithCard({
2667
2670
  const response = await (0, import_core9.getDefaultOnrampToken)(
2668
2671
  {
2669
2672
  country_code: userIpInfo?.alpha2?.toUpperCase() || "US",
2670
- subdivision_code: userIpInfo?.state || void 0,
2673
+ subdivision_code: userIpInfo?.subdivisionCode || void 0,
2671
2674
  token_address: destinationTokenAddress,
2672
2675
  chain_id: destinationChainId,
2673
2676
  chain_type: destinationChainType
@@ -2750,7 +2753,7 @@ function BuyWithCard({
2750
2753
  source_currency: currency.toLowerCase(),
2751
2754
  destination_currency: defaultToken.destination_currency,
2752
2755
  destination_network: defaultToken.destination_network,
2753
- subdivision_code: userIpInfo?.state || void 0
2756
+ subdivision_code: userIpInfo?.subdivisionCode || void 0
2754
2757
  };
2755
2758
  const response = await (0, import_core9.getOnrampQuotes)(request, publishableKey);
2756
2759
  setQuotes(response.data);
@@ -2762,31 +2765,29 @@ function BuyWithCard({
2762
2765
  );
2763
2766
  if (manualProviderStillExists) {
2764
2767
  setSelectedProvider(manualProviderStillExists);
2765
- const bestProvider = response.data.reduce(
2766
- (best, current) => current.destination_amount > best.destination_amount ? current : best
2767
- );
2768
+ const firstProvider = response.data[0];
2768
2769
  if (!autoSelectedProvider) {
2769
- setAutoSelectedProvider(bestProvider.service_provider);
2770
+ setAutoSelectedProvider(firstProvider.service_provider);
2770
2771
  }
2771
2772
  setIsAutoSelected(
2772
2773
  manualProviderStillExists.service_provider === autoSelectedProvider
2773
2774
  );
2774
- } else {
2775
- const bestProvider = response.data.reduce(
2776
- (best, current) => current.destination_amount > best.destination_amount ? current : best
2777
- );
2778
- setSelectedProvider(bestProvider);
2779
- setAutoSelectedProvider(bestProvider.service_provider);
2775
+ } else if (response.data.length > 0) {
2776
+ const firstProvider = response.data[0];
2777
+ setSelectedProvider(firstProvider);
2778
+ setAutoSelectedProvider(firstProvider.service_provider);
2780
2779
  setIsAutoSelected(true);
2781
2780
  setHasManualSelection(false);
2781
+ } else {
2782
+ setSelectedProvider(null);
2783
+ setIsAutoSelected(false);
2784
+ setHasManualSelection(false);
2782
2785
  }
2783
2786
  } else {
2784
2787
  if (response.data.length > 0) {
2785
- const bestProvider = response.data.reduce(
2786
- (best, current) => current.destination_amount > best.destination_amount ? current : best
2787
- );
2788
- setSelectedProvider(bestProvider);
2789
- setAutoSelectedProvider(bestProvider.service_provider);
2788
+ const firstProvider = response.data[0];
2789
+ setSelectedProvider(firstProvider);
2790
+ setAutoSelectedProvider(firstProvider.service_provider);
2790
2791
  setIsAutoSelected(true);
2791
2792
  }
2792
2793
  }
@@ -2859,7 +2860,7 @@ function BuyWithCard({
2859
2860
  destination_currency: selectedProvider.destination_currency,
2860
2861
  destination_network: selectedProvider.destination_network,
2861
2862
  wallet_address: wallet.address,
2862
- subdivision_code: userIpInfo?.state || void 0,
2863
+ subdivision_code: userIpInfo?.subdivisionCode || void 0,
2863
2864
  external_id: externalId
2864
2865
  };
2865
2866
  const sessionStartUrl = (0, import_core9.getOnrampSessionStartUrl)(
@@ -2882,9 +2883,7 @@ function BuyWithCard({
2882
2883
  window.open(sessionStartUrl, "_blank");
2883
2884
  handleViewChange("onramp");
2884
2885
  };
2885
- const sortedQuotes = [...quotes].sort(
2886
- (a, b) => b.destination_amount - a.destination_amount
2887
- );
2886
+ const sortedQuotes = quotes;
2888
2887
  const currencySymbol = getCurrencySymbol(currency);
2889
2888
  return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
2890
2889
  "div",
@@ -6996,16 +6995,27 @@ function useAllowedCountry(publishableKey) {
6996
6995
  let isAllowed = null;
6997
6996
  if (ipData && configData) {
6998
6997
  const blockedCodes = configData.blocked_country_codes || [];
6998
+ const blockedSubdivisions = configData.blocked_country_subdivisions || [];
6999
6999
  const userCountryUpper = ipData.alpha2.toUpperCase();
7000
- isAllowed = !blockedCodes.some(
7000
+ const userSubdivision = ipData.subdivision_code || ipData.state || "";
7001
+ const userSubdivisionUpper = userSubdivision.toUpperCase();
7002
+ const isCountryBlocked = blockedCodes.some(
7001
7003
  (code) => code.toUpperCase() === userCountryUpper
7002
7004
  );
7005
+ const isSubdivisionBlocked = blockedSubdivisions.some((entry) => {
7006
+ if (entry.country_code.toUpperCase() !== userCountryUpper) return false;
7007
+ return entry.subdivision_codes.some(
7008
+ (code) => code.toUpperCase() === userSubdivisionUpper
7009
+ );
7010
+ });
7011
+ isAllowed = !isCountryBlocked && !isSubdivisionBlocked;
7003
7012
  }
7013
+ const subdivisionCode = (ipData?.subdivision_code || ipData?.state || "").toLowerCase() || null;
7004
7014
  return {
7005
7015
  isAllowed,
7006
- // Return lowercase for consistency with useUserIp hook
7007
7016
  alpha2: ipData?.alpha2?.toLowerCase() ?? null,
7008
7017
  country: ipData?.country ?? null,
7018
+ subdivisionCode,
7009
7019
  isLoading,
7010
7020
  error
7011
7021
  };
@@ -12472,19 +12482,30 @@ var import_lucide_react24 = require("lucide-react");
12472
12482
  // src/hooks/use-payment-intent.ts
12473
12483
  var import_react_query9 = require("@tanstack/react-query");
12474
12484
  var import_core24 = require("@unifold/core");
12485
+ var TERMINAL_STATUSES = /* @__PURE__ */ new Set([
12486
+ "succeeded",
12487
+ "expired",
12488
+ "refunded",
12489
+ "canceled"
12490
+ ]);
12475
12491
  function usePaymentIntent(params) {
12476
12492
  const {
12477
12493
  clientSecret,
12478
12494
  publishableKey,
12479
12495
  enabled = true,
12480
- pollingInterval = 5e3
12496
+ pollingInterval = 3e3
12481
12497
  } = params;
12482
12498
  return (0, import_react_query9.useQuery)({
12483
12499
  queryKey: ["unifold", "paymentIntent", clientSecret, publishableKey],
12484
12500
  queryFn: () => (0, import_core24.retrievePaymentIntent)(clientSecret, publishableKey),
12485
12501
  enabled: enabled && !!clientSecret && !!publishableKey,
12486
12502
  staleTime: 0,
12487
- refetchInterval: pollingInterval || false,
12503
+ refetchInterval: (query) => {
12504
+ if (!pollingInterval) return false;
12505
+ const status = query.state.data?.status;
12506
+ if (status && TERMINAL_STATUSES.has(status)) return false;
12507
+ return pollingInterval;
12508
+ },
12488
12509
  refetchOnWindowFocus: true,
12489
12510
  retry: 3,
12490
12511
  retryDelay: (attempt) => Math.min(1e3 * 2 ** attempt, 1e4)
@@ -12615,7 +12636,7 @@ function CheckoutModal({
12615
12636
  clientSecret,
12616
12637
  publishableKey,
12617
12638
  enabled: open && !!clientSecret,
12618
- pollingInterval: 5e3
12639
+ pollingInterval: 3e3
12619
12640
  });
12620
12641
  const { projectConfig } = useProjectConfig({
12621
12642
  publishableKey,
@@ -15236,6 +15257,7 @@ function WithdrawTokenSelector({
15236
15257
  useDepositQuote,
15237
15258
  usePaymentIntent,
15238
15259
  useSourceTokenValidation,
15260
+ useSupportedDepositTokens,
15239
15261
  useSupportedDestinationTokens,
15240
15262
  useTheme,
15241
15263
  useVerifyRecipientAddress,
package/dist/index.mjs CHANGED
@@ -1236,11 +1236,13 @@ function useUserIp() {
1236
1236
  queryKey: ["unifold", "userIpInfo"],
1237
1237
  queryFn: async () => {
1238
1238
  const data = await getIpAddress();
1239
+ const subdivision = (data.subdivision_code || data.state || "").toLowerCase() || null;
1239
1240
  return {
1240
1241
  alpha2: data.alpha2.toLowerCase(),
1241
1242
  alpha3: data.alpha3?.toLowerCase(),
1242
1243
  country: data.country,
1243
- state: data.state?.toLowerCase() ?? null,
1244
+ state: subdivision,
1245
+ subdivisionCode: subdivision,
1244
1246
  ipAddress: data.ip_address
1245
1247
  };
1246
1248
  },
@@ -2591,7 +2593,7 @@ function BuyWithCard({
2591
2593
  const response = await getDefaultOnrampToken(
2592
2594
  {
2593
2595
  country_code: userIpInfo?.alpha2?.toUpperCase() || "US",
2594
- subdivision_code: userIpInfo?.state || void 0,
2596
+ subdivision_code: userIpInfo?.subdivisionCode || void 0,
2595
2597
  token_address: destinationTokenAddress,
2596
2598
  chain_id: destinationChainId,
2597
2599
  chain_type: destinationChainType
@@ -2674,7 +2676,7 @@ function BuyWithCard({
2674
2676
  source_currency: currency.toLowerCase(),
2675
2677
  destination_currency: defaultToken.destination_currency,
2676
2678
  destination_network: defaultToken.destination_network,
2677
- subdivision_code: userIpInfo?.state || void 0
2679
+ subdivision_code: userIpInfo?.subdivisionCode || void 0
2678
2680
  };
2679
2681
  const response = await getOnrampQuotes(request, publishableKey);
2680
2682
  setQuotes(response.data);
@@ -2686,31 +2688,29 @@ function BuyWithCard({
2686
2688
  );
2687
2689
  if (manualProviderStillExists) {
2688
2690
  setSelectedProvider(manualProviderStillExists);
2689
- const bestProvider = response.data.reduce(
2690
- (best, current) => current.destination_amount > best.destination_amount ? current : best
2691
- );
2691
+ const firstProvider = response.data[0];
2692
2692
  if (!autoSelectedProvider) {
2693
- setAutoSelectedProvider(bestProvider.service_provider);
2693
+ setAutoSelectedProvider(firstProvider.service_provider);
2694
2694
  }
2695
2695
  setIsAutoSelected(
2696
2696
  manualProviderStillExists.service_provider === autoSelectedProvider
2697
2697
  );
2698
- } else {
2699
- const bestProvider = response.data.reduce(
2700
- (best, current) => current.destination_amount > best.destination_amount ? current : best
2701
- );
2702
- setSelectedProvider(bestProvider);
2703
- setAutoSelectedProvider(bestProvider.service_provider);
2698
+ } else if (response.data.length > 0) {
2699
+ const firstProvider = response.data[0];
2700
+ setSelectedProvider(firstProvider);
2701
+ setAutoSelectedProvider(firstProvider.service_provider);
2704
2702
  setIsAutoSelected(true);
2705
2703
  setHasManualSelection(false);
2704
+ } else {
2705
+ setSelectedProvider(null);
2706
+ setIsAutoSelected(false);
2707
+ setHasManualSelection(false);
2706
2708
  }
2707
2709
  } else {
2708
2710
  if (response.data.length > 0) {
2709
- const bestProvider = response.data.reduce(
2710
- (best, current) => current.destination_amount > best.destination_amount ? current : best
2711
- );
2712
- setSelectedProvider(bestProvider);
2713
- setAutoSelectedProvider(bestProvider.service_provider);
2711
+ const firstProvider = response.data[0];
2712
+ setSelectedProvider(firstProvider);
2713
+ setAutoSelectedProvider(firstProvider.service_provider);
2714
2714
  setIsAutoSelected(true);
2715
2715
  }
2716
2716
  }
@@ -2783,7 +2783,7 @@ function BuyWithCard({
2783
2783
  destination_currency: selectedProvider.destination_currency,
2784
2784
  destination_network: selectedProvider.destination_network,
2785
2785
  wallet_address: wallet.address,
2786
- subdivision_code: userIpInfo?.state || void 0,
2786
+ subdivision_code: userIpInfo?.subdivisionCode || void 0,
2787
2787
  external_id: externalId
2788
2788
  };
2789
2789
  const sessionStartUrl = getOnrampSessionStartUrl(
@@ -2806,9 +2806,7 @@ function BuyWithCard({
2806
2806
  window.open(sessionStartUrl, "_blank");
2807
2807
  handleViewChange("onramp");
2808
2808
  };
2809
- const sortedQuotes = [...quotes].sort(
2810
- (a, b) => b.destination_amount - a.destination_amount
2811
- );
2809
+ const sortedQuotes = quotes;
2812
2810
  const currencySymbol = getCurrencySymbol(currency);
2813
2811
  return /* @__PURE__ */ jsxs8(
2814
2812
  "div",
@@ -6933,16 +6931,27 @@ function useAllowedCountry(publishableKey) {
6933
6931
  let isAllowed = null;
6934
6932
  if (ipData && configData) {
6935
6933
  const blockedCodes = configData.blocked_country_codes || [];
6934
+ const blockedSubdivisions = configData.blocked_country_subdivisions || [];
6936
6935
  const userCountryUpper = ipData.alpha2.toUpperCase();
6937
- isAllowed = !blockedCodes.some(
6936
+ const userSubdivision = ipData.subdivision_code || ipData.state || "";
6937
+ const userSubdivisionUpper = userSubdivision.toUpperCase();
6938
+ const isCountryBlocked = blockedCodes.some(
6938
6939
  (code) => code.toUpperCase() === userCountryUpper
6939
6940
  );
6941
+ const isSubdivisionBlocked = blockedSubdivisions.some((entry) => {
6942
+ if (entry.country_code.toUpperCase() !== userCountryUpper) return false;
6943
+ return entry.subdivision_codes.some(
6944
+ (code) => code.toUpperCase() === userSubdivisionUpper
6945
+ );
6946
+ });
6947
+ isAllowed = !isCountryBlocked && !isSubdivisionBlocked;
6940
6948
  }
6949
+ const subdivisionCode = (ipData?.subdivision_code || ipData?.state || "").toLowerCase() || null;
6941
6950
  return {
6942
6951
  isAllowed,
6943
- // Return lowercase for consistency with useUserIp hook
6944
6952
  alpha2: ipData?.alpha2?.toLowerCase() ?? null,
6945
6953
  country: ipData?.country ?? null,
6954
+ subdivisionCode,
6946
6955
  isLoading,
6947
6956
  error
6948
6957
  };
@@ -12451,19 +12460,30 @@ import { AlertTriangle as AlertTriangle2, ChevronRight as ChevronRight12 } from
12451
12460
  // src/hooks/use-payment-intent.ts
12452
12461
  import { useQuery as useQuery9 } from "@tanstack/react-query";
12453
12462
  import { retrievePaymentIntent } from "@unifold/core";
12463
+ var TERMINAL_STATUSES = /* @__PURE__ */ new Set([
12464
+ "succeeded",
12465
+ "expired",
12466
+ "refunded",
12467
+ "canceled"
12468
+ ]);
12454
12469
  function usePaymentIntent(params) {
12455
12470
  const {
12456
12471
  clientSecret,
12457
12472
  publishableKey,
12458
12473
  enabled = true,
12459
- pollingInterval = 5e3
12474
+ pollingInterval = 3e3
12460
12475
  } = params;
12461
12476
  return useQuery9({
12462
12477
  queryKey: ["unifold", "paymentIntent", clientSecret, publishableKey],
12463
12478
  queryFn: () => retrievePaymentIntent(clientSecret, publishableKey),
12464
12479
  enabled: enabled && !!clientSecret && !!publishableKey,
12465
12480
  staleTime: 0,
12466
- refetchInterval: pollingInterval || false,
12481
+ refetchInterval: (query) => {
12482
+ if (!pollingInterval) return false;
12483
+ const status = query.state.data?.status;
12484
+ if (status && TERMINAL_STATUSES.has(status)) return false;
12485
+ return pollingInterval;
12486
+ },
12467
12487
  refetchOnWindowFocus: true,
12468
12488
  retry: 3,
12469
12489
  retryDelay: (attempt) => Math.min(1e3 * 2 ** attempt, 1e4)
@@ -12596,7 +12616,7 @@ function CheckoutModal({
12596
12616
  clientSecret,
12597
12617
  publishableKey,
12598
12618
  enabled: open && !!clientSecret,
12599
- pollingInterval: 5e3
12619
+ pollingInterval: 3e3
12600
12620
  });
12601
12621
  const { projectConfig } = useProjectConfig({
12602
12622
  publishableKey,
@@ -15254,6 +15274,7 @@ export {
15254
15274
  useDepositQuote,
15255
15275
  usePaymentIntent,
15256
15276
  useSourceTokenValidation,
15277
+ useSupportedDepositTokens,
15257
15278
  useSupportedDestinationTokens,
15258
15279
  useTheme,
15259
15280
  useVerifyRecipientAddress,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unifold/ui-react",
3
- "version": "0.1.49",
3
+ "version": "0.1.51",
4
4
  "description": "Unifold UI React - Deposit and onramp components for React applications",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -34,7 +34,7 @@
34
34
  "lucide-react": "^0.454.0",
35
35
  "qr-code-styling": "^1.6.0-rc.1",
36
36
  "tailwind-merge": "^2.0.0",
37
- "@unifold/core": "0.1.49"
37
+ "@unifold/core": "0.1.51"
38
38
  },
39
39
  "devDependencies": {
40
40
  "@solana/web3.js": "^1.87.0",