@unifold/ui-react 0.1.68-beta.1 → 0.1.68

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 useCallback8,
7
7
  useRef as useRef12,
8
- useMemo as useMemo12
8
+ useMemo as useMemo13
9
9
  } from "react";
10
10
  import { ChevronRight as ChevronRight18, MapPinOff as MapPinOff2, AlertTriangle as AlertTriangle3, Bitcoin, DollarSign as DollarSign3 } from "lucide-react";
11
11
 
@@ -465,6 +465,7 @@ var DialogContent = React3.forwardRef(({ className, style, children, omitOverlay
465
465
  className: cn(
466
466
  portalContainer ? "uf-absolute" : "uf-fixed",
467
467
  "uf-bottom-0 uf-left-0 uf-right-0 uf-top-0 uf-z-50 uf-grid uf-w-full uf-max-w-full uf-h-full",
468
+ "focus:uf-outline-none focus-visible:uf-outline-none",
468
469
  !portalContainer && "sm:uf-left-[50%] sm:uf-top-[50%] sm:uf-bottom-auto sm:uf-right-auto sm:uf-translate-x-[-50%] sm:uf-translate-y-[-50%] sm:uf-h-auto",
469
470
  "uf-border uf-bg-background",
470
471
  omitOverlayEmbed ? "uf-gap-0 uf-p-0" : "uf-gap-4 uf-p-6 uf-shadow-lg uf-duration-200",
@@ -480,6 +481,14 @@ var DialogContent = React3.forwardRef(({ className, style, children, omitOverlay
480
481
  ),
481
482
  style: {
482
483
  "--uf-container-radius": `${components.container.borderRadius}px`,
484
+ // Radix traps focus and, when the focused control unmounts during an
485
+ // in-modal screen transition, moves focus onto this container. Modern
486
+ // browsers paint the default focus ring via `:focus-visible`, which
487
+ // flashes a highlight around the whole modal until focus settles on
488
+ // the next screen. Suppress it inline so it works regardless of the
489
+ // Tailwind build (the container is a programmatic focus target, not an
490
+ // interactive control, so it should never show a focus ring).
491
+ outline: "none",
483
492
  ...portalContainer ? { display: "flex", flexDirection: "column" } : {},
484
493
  ...style
485
494
  },
@@ -1487,7 +1496,7 @@ var en_default = {
1487
1496
  payWithStripeLink: {
1488
1497
  success: {
1489
1498
  processingTitle: "Payment confirmed",
1490
- processingSubtitle: "We\u2019ve received your payment and are preparing your purchase. This will only take a moment.",
1499
+ processingSubtitle: "We\u2019ve received your payment and are sending your purchase to your wallet. This usually takes just a few minutes.",
1491
1500
  completeTitle: "Your purchase is on the way",
1492
1501
  completeSubtitle: "Your purchase is complete and is being sent to your wallet.",
1493
1502
  failedTitle: "Purchase didn\u2019t go through",
@@ -3520,37 +3529,21 @@ function BuyWithCard({
3520
3529
 
3521
3530
  // src/components/deposits/BuyWithApplePay.tsx
3522
3531
  import * as React5 from "react";
3523
- import { useCallback, useEffect as useEffect7, useMemo as useMemo2, useRef as useRef3, useState as useState11 } from "react";
3524
- import { Loader2 as Loader22, AlertCircle } from "lucide-react";
3532
+ import { useCallback, useEffect as useEffect7, useMemo as useMemo3, useRef as useRef3, useState as useState11 } from "react";
3533
+ import { Loader2 as Loader22 } from "lucide-react";
3525
3534
  import {
3526
3535
  createCoinbaseApplePaySession,
3527
3536
  createOnrampVerificationSession,
3528
3537
  exchangeOnrampVerificationToken,
3529
3538
  sendOnrampVerificationOtp,
3530
3539
  verifyOnrampVerificationOtp,
3540
+ getCoinbaseApplePayLimits as getCoinbaseApplePayLimits2,
3541
+ requestCoinbaseApplePayLimitUpgrade,
3531
3542
  generatePrefixedKSUID as generatePrefixedKSUID3,
3532
3543
  getWalletByChainType as getWalletByChainType2,
3533
3544
  DepositEventType as DepositEventType3
3534
3545
  } from "@unifold/core";
3535
3546
 
3536
- // src/hooks/use-project-config.ts
3537
- import { useQuery as useQuery3 } from "@tanstack/react-query";
3538
- import { getProjectConfig } from "@unifold/core";
3539
- function useProjectConfig({
3540
- publishableKey,
3541
- enabled = true
3542
- }) {
3543
- const { data: projectConfig, isLoading } = useQuery3({
3544
- queryKey: ["unifold", "projectConfig", publishableKey],
3545
- queryFn: () => getProjectConfig(publishableKey),
3546
- enabled,
3547
- staleTime: 1e3 * 60 * 30,
3548
- refetchOnMount: true,
3549
- refetchOnWindowFocus: true
3550
- });
3551
- return { projectConfig, isLoading };
3552
- }
3553
-
3554
3547
  // src/lib/apple-pay.ts
3555
3548
  var STORAGE_KEY_PREFIX = "unifold:apple-pay:session";
3556
3549
  var PHONE_FRESH_MS = 55 * 24 * 60 * 60 * 1e3;
@@ -3586,12 +3579,6 @@ function clearStoredApplePaySession(userId) {
3586
3579
  } catch {
3587
3580
  }
3588
3581
  }
3589
- function isPhoneVerificationFresh(contact) {
3590
- if (!contact?.phoneVerifiedAt) return false;
3591
- const verifiedAt = Date.parse(contact.phoneVerifiedAt);
3592
- if (Number.isNaN(verifiedAt)) return false;
3593
- return Date.now() - verifiedAt < PHONE_FRESH_MS;
3594
- }
3595
3582
  var ONRAMP_TOKEN_EXPIRY_MARGIN_MS = 6e4;
3596
3583
  function isOnrampTokenFresh(contact) {
3597
3584
  if (!contact?.onrampToken || !contact.onrampTokenExpiresAtMs) return false;
@@ -3599,13 +3586,13 @@ function isOnrampTokenFresh(contact) {
3599
3586
  }
3600
3587
 
3601
3588
  // src/hooks/use-coinbase-legal-agreements.ts
3602
- import { useQuery as useQuery4 } from "@tanstack/react-query";
3589
+ import { useQuery as useQuery3 } from "@tanstack/react-query";
3603
3590
  import { getCoinbaseLegalAgreements } from "@unifold/core";
3604
3591
  function useCoinbaseLegalAgreements({
3605
3592
  publishableKey,
3606
3593
  enabled = true
3607
3594
  }) {
3608
- return useQuery4({
3595
+ return useQuery3({
3609
3596
  queryKey: ["unifold", "coinbaseLegalAgreements", publishableKey],
3610
3597
  queryFn: () => getCoinbaseLegalAgreements(publishableKey),
3611
3598
  enabled: enabled && !!publishableKey,
@@ -3617,49 +3604,215 @@ function useCoinbaseLegalAgreements({
3617
3604
  });
3618
3605
  }
3619
3606
 
3607
+ // src/hooks/use-apple-pay-initial-screen.ts
3608
+ import { useMemo as useMemo2 } from "react";
3609
+
3610
+ // src/hooks/use-apple-pay-limits.ts
3611
+ import { useQuery as useQuery4 } from "@tanstack/react-query";
3612
+ import { getCoinbaseApplePayLimits } from "@unifold/core";
3613
+ var US_E164_REGEX = /^\+1\d{10}$/;
3614
+ function useApplePayLimits({
3615
+ phone,
3616
+ publishableKey,
3617
+ enabled = true
3618
+ }) {
3619
+ const phoneValid = US_E164_REGEX.test(phone);
3620
+ return useQuery4({
3621
+ queryKey: ["unifold", "applePayLimits", phone, publishableKey],
3622
+ queryFn: ({ signal }) => getCoinbaseApplePayLimits(phone, publishableKey, signal),
3623
+ enabled: enabled && phoneValid && !!publishableKey,
3624
+ // Coinbase's caps don't change second-to-second; 30s is plenty fresh
3625
+ // for the "user opened the modal, edited their phone, opened it
3626
+ // again" usage pattern, while keeping us off their API on rapid
3627
+ // tap-throughs.
3628
+ staleTime: 3e4,
3629
+ gcTime: 5 * 6e4,
3630
+ refetchOnWindowFocus: false,
3631
+ retry: false
3632
+ });
3633
+ }
3634
+
3635
+ // src/hooks/use-apple-pay-initial-screen.ts
3636
+ var PHONE_REGEX = /^\+1\d{10}$/;
3637
+ var EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
3638
+ function getViewForLimitStatus(limits) {
3639
+ if (!limits.limit_reached) return void 0;
3640
+ switch (limits.upgrade_status) {
3641
+ case "unrequested":
3642
+ return "limit_upgrade_intro";
3643
+ case "resubmit":
3644
+ return "limit_upgrade_form";
3645
+ case "pending":
3646
+ return "limit_upgrade_submitting";
3647
+ case "active":
3648
+ return void 0;
3649
+ default:
3650
+ return "guest_limit_reached";
3651
+ }
3652
+ }
3653
+ function useApplePayInitialScreen({
3654
+ userId,
3655
+ publishableKey,
3656
+ normalizedPhone,
3657
+ userEmail,
3658
+ sessionPhoneVerified,
3659
+ isWaiting
3660
+ }) {
3661
+ const initialStoredSession = useMemo2(() => getStoredApplePaySession(userId), [userId]);
3662
+ const phoneVerified = PHONE_REGEX.test(normalizedPhone) && (sessionPhoneVerified || initialStoredSession?.phone === normalizedPhone && !!initialStoredSession?.phoneVerifiedAt);
3663
+ const {
3664
+ data: applePayLimits,
3665
+ isLoading: applePayLimitsLoading,
3666
+ refetch: refetchApplePayLimits
3667
+ } = useApplePayLimits({
3668
+ phone: normalizedPhone,
3669
+ publishableKey,
3670
+ enabled: phoneVerified
3671
+ });
3672
+ const action = useMemo2(() => {
3673
+ if (isWaiting || applePayLimitsLoading) return { kind: "pending" };
3674
+ const stored = initialStoredSession;
3675
+ const hasUserEmail = !!userEmail && EMAIL_REGEX.test(userEmail);
3676
+ if (!stored) {
3677
+ return { kind: "navigate", view: hasUserEmail ? "phone_input" : "email_input" };
3678
+ }
3679
+ if (hasUserEmail && stored.email.toLowerCase() !== userEmail.toLowerCase()) {
3680
+ return { kind: "navigate", view: "phone_input" };
3681
+ }
3682
+ if (applePayLimits) {
3683
+ const limitView = getViewForLimitStatus(applePayLimits);
3684
+ if (limitView) {
3685
+ return { kind: "limit_upgrade", view: limitView, status: applePayLimits.upgrade_status };
3686
+ }
3687
+ }
3688
+ if (isOnrampTokenFresh(stored) && stored.onrampToken && stored.onrampTokenExpiresAtMs) {
3689
+ return {
3690
+ kind: "resume_with_token",
3691
+ token: {
3692
+ token: stored.onrampToken,
3693
+ token_type: "Bearer",
3694
+ expires_in: Math.max(0, Math.floor((stored.onrampTokenExpiresAtMs - Date.now()) / 1e3))
3695
+ }
3696
+ };
3697
+ }
3698
+ return { kind: "resume_send_sms" };
3699
+ }, [isWaiting, applePayLimitsLoading, initialStoredSession, userEmail, applePayLimits]);
3700
+ return {
3701
+ action,
3702
+ applePayLimits,
3703
+ applePayLimitsLoading,
3704
+ refetchApplePayLimits,
3705
+ phoneVerified
3706
+ };
3707
+ }
3708
+
3709
+ // src/hooks/use-default-onramp-token.ts
3710
+ import { useQuery as useQuery5 } from "@tanstack/react-query";
3711
+ import { getDefaultOnrampToken as getDefaultOnrampToken2 } from "@unifold/core";
3712
+ function useDefaultOnrampToken({
3713
+ publishableKey,
3714
+ tokenAddress,
3715
+ chainId,
3716
+ chainType,
3717
+ countryCode,
3718
+ subdivisionCode,
3719
+ enabled = true
3720
+ }) {
3721
+ const {
3722
+ data: defaultToken,
3723
+ isLoading,
3724
+ isError,
3725
+ error
3726
+ } = useQuery5({
3727
+ queryKey: [
3728
+ "unifold",
3729
+ "defaultOnrampToken",
3730
+ publishableKey,
3731
+ tokenAddress,
3732
+ chainId,
3733
+ chainType,
3734
+ countryCode,
3735
+ subdivisionCode
3736
+ ],
3737
+ queryFn: () => getDefaultOnrampToken2(
3738
+ {
3739
+ token_address: tokenAddress,
3740
+ chain_id: chainId,
3741
+ chain_type: chainType,
3742
+ country_code: countryCode,
3743
+ subdivision_code: subdivisionCode
3744
+ },
3745
+ publishableKey
3746
+ ),
3747
+ enabled: enabled && !!tokenAddress && !!chainId && !!chainType,
3748
+ staleTime: 1e3 * 60 * 60,
3749
+ refetchOnMount: false,
3750
+ refetchOnWindowFocus: false
3751
+ });
3752
+ return { defaultToken, isLoading, isError, error: error ?? null };
3753
+ }
3754
+
3620
3755
  // src/components/deposits/BuyWithApplePay.tsx
3621
3756
  import { Fragment as Fragment4, jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
3622
3757
  var COINBASE_PAY_ORIGIN = "https://pay.coinbase.com";
3758
+ var COINBASE_ALLOWED_POSTMESSAGE_ORIGINS = /* @__PURE__ */ new Set([
3759
+ COINBASE_PAY_ORIGIN,
3760
+ "https://www.coinbase.com"
3761
+ ]);
3762
+ var UNIFOLD_SUPPORT_URL = "https://unifold.io/support";
3623
3763
  var DEFAULT_MIN_USD = 5;
3624
3764
  var DEFAULT_MAX_USD = 500;
3625
3765
  var DEFAULT_SUGGESTED = [25, 50, 100];
3626
- var PHONE_REGEX = /^\+1\d{10}$/;
3627
- var EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
3628
- function formatPhoneInput(raw) {
3629
- const digits = raw.replace(/\D/g, "");
3630
- if (raw.startsWith("+")) {
3631
- return `+${digits}`;
3766
+ var PHONE_REGEX2 = /^\+1\d{10}$/;
3767
+ var EMAIL_REGEX2 = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
3768
+ function parseCoinbasePostMessage(raw) {
3769
+ const parseString = (s) => {
3770
+ try {
3771
+ return JSON.parse(s);
3772
+ } catch {
3773
+ return null;
3774
+ }
3775
+ };
3776
+ const asObject = (value) => value && typeof value === "object" ? value : null;
3777
+ let candidate = raw;
3778
+ if (typeof candidate === "string") {
3779
+ candidate = parseString(candidate);
3632
3780
  }
3633
- if (digits.length === 10) return `+1${digits}`;
3634
- if (digits.length === 11 && digits.startsWith("1")) return `+${digits}`;
3635
- return digits.length > 0 ? `+${digits}` : "";
3636
- }
3637
- function maskEmail(email) {
3638
- const [local, domain] = email.split("@");
3639
- if (!local || !domain) return email;
3640
- const visible = local.slice(0, Math.min(2, local.length));
3641
- return `${visible}${"*".repeat(Math.max(local.length - visible.length, 1))}@${domain}`;
3642
- }
3643
- function maskPhone(phone) {
3644
- if (!/^\+1\d{10}$/.test(phone)) return phone;
3645
- return `+1 (***) ***-${phone.slice(-4)}`;
3781
+ const obj = asObject(candidate);
3782
+ if (!obj) return null;
3783
+ const nestedData = obj.data;
3784
+ let eventName = typeof obj.eventName === "string" && obj.eventName || typeof obj.event_name === "string" && obj.event_name || typeof obj.type === "string" && obj.type || null;
3785
+ let dataObj = asObject(nestedData);
3786
+ if (!eventName && typeof nestedData === "string") {
3787
+ const nestedParsed = parseString(nestedData);
3788
+ const nestedObj = asObject(nestedParsed);
3789
+ if (nestedObj) {
3790
+ eventName = typeof nestedObj.eventName === "string" && nestedObj.eventName || typeof nestedObj.event_name === "string" && nestedObj.event_name || typeof nestedObj.type === "string" && nestedObj.type || null;
3791
+ dataObj = asObject(nestedObj.data);
3792
+ }
3793
+ }
3794
+ if (!eventName || !/^onramp_api\./i.test(eventName)) return null;
3795
+ return {
3796
+ eventName,
3797
+ data: dataObj ? {
3798
+ errorCode: typeof dataObj.errorCode === "string" ? dataObj.errorCode : typeof dataObj.error_code === "string" ? dataObj.error_code : void 0,
3799
+ errorMessage: typeof dataObj.errorMessage === "string" ? dataObj.errorMessage : typeof dataObj.error_message === "string" ? dataObj.error_message : void 0
3800
+ } : void 0
3801
+ };
3646
3802
  }
3647
3803
  var BuyWithApplePay = React5.forwardRef(
3648
3804
  function BuyWithApplePay2({
3649
3805
  userId,
3650
3806
  publishableKey,
3651
- recipientAddress,
3652
3807
  destinationChainType,
3653
3808
  destinationChainId,
3654
3809
  destinationTokenAddress,
3655
- destinationTokenSymbol = "USDC",
3656
- externalUserEmail,
3657
- enableApplePay,
3810
+ userEmail,
3658
3811
  amountUsd,
3659
3812
  minAmountUsd = DEFAULT_MIN_USD,
3660
3813
  maxAmountUsd = DEFAULT_MAX_USD,
3661
3814
  suggestedAmountsUsd = DEFAULT_SUGGESTED,
3662
- wallets: externalWallets,
3815
+ wallets: depositWallets,
3663
3816
  onDepositSuccess,
3664
3817
  onDepositError,
3665
3818
  onEvent,
@@ -3668,48 +3821,61 @@ var BuyWithApplePay = React5.forwardRef(
3668
3821
  onExit
3669
3822
  }, ref) {
3670
3823
  const { colors: colors2, fonts, components } = useTheme();
3671
- const { projectConfig, isLoading: configLoading } = useProjectConfig({ publishableKey });
3672
- const applePayEnabled = enableApplePay ?? projectConfig?.apple_pay?.enabled ?? true;
3673
3824
  const [view, setView] = useState11("loading_config");
3674
3825
  const [errorMessage, setErrorMessage] = useState11("");
3675
- const [email, setEmail] = useState11(externalUserEmail ?? "");
3676
- const [phoneInput, setPhoneInput] = useState11("");
3677
- const [, setTosAccepted] = useState11(false);
3826
+ const [email, setEmail] = useState11(() => {
3827
+ if (userEmail && EMAIL_REGEX2.test(userEmail)) return userEmail;
3828
+ return getStoredApplePaySession(userId)?.email ?? "";
3829
+ });
3830
+ const [phoneInput, setPhoneInput] = useState11(() => {
3831
+ const stored = getStoredApplePaySession(userId);
3832
+ if (!stored) return "";
3833
+ if (userEmail && EMAIL_REGEX2.test(userEmail) && stored.email.toLowerCase() !== userEmail.toLowerCase()) {
3834
+ return "";
3835
+ }
3836
+ return stored.phone;
3837
+ });
3678
3838
  const { data: legalAgreementsData, isLoading: legalAgreementsLoading } = useCoinbaseLegalAgreements({
3679
- publishableKey,
3680
- enabled: applePayEnabled
3839
+ publishableKey
3681
3840
  });
3682
3841
  const legalAgreements = legalAgreementsData ?? null;
3683
3842
  const [verificationSession, setVerificationSession] = useState11(null);
3684
3843
  const [clientSecret, setClientSecret] = useState11(null);
3685
- const [emailCode, setEmailCode] = useState11("");
3686
3844
  const [phoneCode, setPhoneCode] = useState11("");
3687
3845
  const [otpError, setOtpError] = useState11(null);
3688
3846
  const [otpSubmitting, setOtpSubmitting] = useState11(false);
3689
3847
  const [resendCooldown, setResendCooldown] = useState11(0);
3848
+ const [dobInput, setDobInput] = useState11("");
3849
+ const [ssnLast4, setSsnLast4] = useState11("");
3850
+ const [limitUpgradeError, setLimitUpgradeError] = useState11(null);
3851
+ const [upgradeSubmitting, setUpgradeSubmitting] = useState11(false);
3852
+ const upgradeFlowAbortRef = useRef3(null);
3690
3853
  const [amount, setAmount] = useState11(amountUsd ?? "");
3691
3854
  const [paymentSession, setPaymentSession] = useState11(
3692
3855
  null
3693
3856
  );
3694
3857
  const [paymentSessionAmount, setPaymentSessionAmount] = useState11(null);
3695
- const [preparingSession, setPreparingSession] = useState11(false);
3696
3858
  const popupRef = useRef3(null);
3697
3859
  const sessionAbortRef = useRef3(null);
3698
3860
  const [onrampToken, setOnrampToken] = useState11(null);
3699
- const { data: depositAddressResponse } = useDepositAddress({
3700
- userId,
3861
+ const { userIpInfo, isLoading: userIpInfoLoading } = useUserIp();
3862
+ const {
3863
+ defaultToken,
3864
+ isLoading: defaultTokenLoading,
3865
+ isError: defaultTokenError
3866
+ } = useDefaultOnrampToken({
3701
3867
  publishableKey,
3702
- recipientAddress,
3703
- destinationChainType,
3704
- destinationChainId,
3705
- destinationTokenAddress,
3706
- enabled: !externalWallets?.length
3868
+ tokenAddress: destinationTokenAddress,
3869
+ chainId: destinationChainId,
3870
+ chainType: destinationChainType,
3871
+ countryCode: userIpInfo?.alpha2,
3872
+ subdivisionCode: userIpInfo?.subdivisionCode ?? void 0
3707
3873
  });
3708
- const wallets = externalWallets?.length ? externalWallets : depositAddressResponse?.data ?? [];
3709
- const depositWallet = useMemo2(() => {
3710
- if (!destinationChainType) return void 0;
3711
- return getWalletByChainType2(wallets, destinationChainType);
3712
- }, [wallets, destinationChainType]);
3874
+ const depositWallet = useMemo3(() => {
3875
+ const routingChainType = defaultToken?.destination_token_metadata?.chain_type;
3876
+ if (!routingChainType) return void 0;
3877
+ return getWalletByChainType2(depositWallets ?? [], routingChainType);
3878
+ }, [depositWallets, defaultToken]);
3713
3879
  const depositWalletId = depositWallet?.id;
3714
3880
  const { executions } = useDepositPolling({
3715
3881
  userId,
@@ -3733,49 +3899,10 @@ var BuyWithApplePay = React5.forwardRef(
3733
3899
  onDepositError(error);
3734
3900
  } : void 0
3735
3901
  });
3736
- const [emailLocked, setEmailLocked] = useState11(false);
3737
- useEffect7(() => {
3738
- if (configLoading) return;
3739
- if (!applePayEnabled) {
3740
- setView("disabled");
3741
- return;
3742
- }
3743
- setTosAccepted(true);
3744
- const applyStoredOnrampToken = (stored2) => {
3745
- if (!isOnrampTokenFresh(stored2) || !stored2.onrampToken || !stored2.onrampTokenExpiresAtMs) {
3746
- return false;
3747
- }
3748
- setOnrampToken({
3749
- token: stored2.onrampToken,
3750
- token_type: "Bearer",
3751
- expires_in: Math.max(0, Math.floor((stored2.onrampTokenExpiresAtMs - Date.now()) / 1e3))
3752
- });
3753
- setView("amount");
3754
- return true;
3755
- };
3756
- if (externalUserEmail && EMAIL_REGEX.test(externalUserEmail)) {
3757
- setEmail(externalUserEmail);
3758
- setEmailLocked(true);
3759
- const stored2 = getStoredApplePaySession(userId);
3760
- if (stored2?.email?.toLowerCase() === externalUserEmail.toLowerCase()) {
3761
- setPhoneInput(stored2.phone);
3762
- if (applyStoredOnrampToken(stored2)) return;
3763
- }
3764
- setView("phone_input");
3765
- return;
3766
- }
3767
- const stored = getStoredApplePaySession(userId);
3768
- if (stored) {
3769
- setEmail(stored.email);
3770
- setPhoneInput(stored.phone);
3771
- setEmailLocked(true);
3772
- if (applyStoredOnrampToken(stored)) return;
3773
- setView("phone_input");
3774
- return;
3775
- }
3776
- setEmailLocked(false);
3777
- setView("email_input");
3778
- }, [configLoading, applePayEnabled, externalUserEmail, userId]);
3902
+ const [emailLocked, setEmailLocked] = useState11(() => {
3903
+ if (userEmail && EMAIL_REGEX2.test(userEmail)) return true;
3904
+ return !!getStoredApplePaySession(userId);
3905
+ });
3779
3906
  useEffect7(() => {
3780
3907
  if (resendCooldown <= 0) return;
3781
3908
  const t13 = setInterval(() => setResendCooldown((c) => Math.max(0, c - 1)), 1e3);
@@ -3784,19 +3911,10 @@ var BuyWithApplePay = React5.forwardRef(
3784
3911
  useEffect7(() => {
3785
3912
  if (view !== "checking_deposit") return;
3786
3913
  const onMessage = (event) => {
3787
- if (event.origin !== COINBASE_PAY_ORIGIN) return;
3788
- let parsed = null;
3789
- const data = event.data;
3790
- if (typeof data === "string") {
3791
- try {
3792
- parsed = JSON.parse(data);
3793
- } catch {
3794
- return;
3795
- }
3796
- } else if (data && typeof data === "object" && "eventName" in data) {
3797
- parsed = data;
3798
- }
3914
+ if (!COINBASE_ALLOWED_POSTMESSAGE_ORIGINS.has(event.origin)) return;
3915
+ const parsed = parseCoinbasePostMessage(event.data);
3799
3916
  if (!parsed) return;
3917
+ const eventName = parsed.eventName.toLowerCase();
3800
3918
  const code = parsed.data?.errorCode;
3801
3919
  const closePopup = () => {
3802
3920
  try {
@@ -3805,16 +3923,20 @@ var BuyWithApplePay = React5.forwardRef(
3805
3923
  }
3806
3924
  popupRef.current = null;
3807
3925
  };
3808
- switch (parsed.eventName) {
3926
+ switch (eventName) {
3809
3927
  case "onramp_api.commit_success":
3810
3928
  case "onramp_api.polling_success":
3929
+ case "onramp_api.cancel":
3811
3930
  closePopup();
3812
3931
  break;
3813
3932
  case "onramp_api.commit_error":
3933
+ case "onramp_api.polling_failed":
3814
3934
  if (code === "ERROR_CODE_GUEST_TRANSACTION_COUNT" || code === "ERROR_CODE_GUEST_DAILY_TRANSACTION_COUNT" || code === "ERROR_CODE_GUEST_TRANSACTION_LIMIT") {
3815
3935
  closePopup();
3816
3936
  setView("guest_limit_reached");
3937
+ return;
3817
3938
  }
3939
+ closePopup();
3818
3940
  break;
3819
3941
  default:
3820
3942
  break;
@@ -3826,17 +3948,17 @@ var BuyWithApplePay = React5.forwardRef(
3826
3948
  useEffect7(() => {
3827
3949
  onViewChange?.(view);
3828
3950
  }, [view, onViewChange]);
3829
- useEffect7(
3830
- () => () => {
3951
+ useEffect7(() => {
3952
+ return () => {
3831
3953
  sessionAbortRef.current?.abort();
3954
+ upgradeFlowAbortRef.current?.abort();
3832
3955
  try {
3833
3956
  popupRef.current?.close();
3834
3957
  } catch {
3835
3958
  }
3836
3959
  popupRef.current = null;
3837
- },
3838
- []
3839
- );
3960
+ };
3961
+ }, []);
3840
3962
  React5.useImperativeHandle(
3841
3963
  ref,
3842
3964
  () => ({
@@ -3846,14 +3968,20 @@ var BuyWithApplePay = React5.forwardRef(
3846
3968
  setView("email_input");
3847
3969
  return true;
3848
3970
  }
3849
- if (view === "email_otp") {
3850
- setView("phone_input");
3851
- setEmailCode("");
3852
- return true;
3853
- }
3854
3971
  if (view === "phone_otp") {
3855
3972
  setView("phone_input");
3856
3973
  setPhoneCode("");
3974
+ setOtpSubmitting(false);
3975
+ return true;
3976
+ }
3977
+ if (view === "limit_upgrade_intro" || view === "limit_upgrade_failed") {
3978
+ return false;
3979
+ }
3980
+ if (view === "limit_upgrade_form") {
3981
+ setView("limit_upgrade_intro");
3982
+ return true;
3983
+ }
3984
+ if (view === "limit_upgrade_submitting") {
3857
3985
  return true;
3858
3986
  }
3859
3987
  if (view === "checking_deposit") {
@@ -3873,22 +4001,21 @@ var BuyWithApplePay = React5.forwardRef(
3873
4001
  }),
3874
4002
  [view, emailLocked]
3875
4003
  );
3876
- const normalizedPhone = useMemo2(() => formatPhoneInput(phoneInput), [phoneInput]);
3877
- const isContactValid = EMAIL_REGEX.test(email) && PHONE_REGEX.test(normalizedPhone);
3878
- const persistContact = useCallback(
4004
+ const normalizedPhone = useMemo3(() => formatPhoneInput(phoneInput), [phoneInput]);
4005
+ const isContactValid = EMAIL_REGEX2.test(email) && PHONE_REGEX2.test(normalizedPhone);
4006
+ const storeApplePaySession = useCallback(
3879
4007
  (patch = {}) => {
3880
- const base = {
4008
+ const session = {
3881
4009
  email,
3882
4010
  phone: normalizedPhone,
3883
4011
  termsAcceptedAt: (/* @__PURE__ */ new Date()).toISOString(),
3884
4012
  ...patch
3885
4013
  };
3886
- setStoredApplePaySession(userId, base);
4014
+ setStoredApplePaySession(userId, session);
3887
4015
  },
3888
4016
  [email, normalizedPhone, userId]
3889
4017
  );
3890
- const startVerification = useCallback(async () => {
3891
- if (!isContactValid) return;
4018
+ const createSessionAndSendOtp = useCallback(async () => {
3892
4019
  setView("submitting_session");
3893
4020
  setErrorMessage("");
3894
4021
  try {
@@ -3906,71 +4033,192 @@ var BuyWithApplePay = React5.forwardRef(
3906
4033
  throw new Error("Server did not return a client_secret");
3907
4034
  }
3908
4035
  setClientSecret(session.client_secret);
3909
- persistContact();
3910
- if (session.email.status === "verified") {
3911
- await sendOnrampVerificationOtp(
3912
- session.id,
3913
- "phone",
3914
- session.client_secret,
3915
- publishableKey
3916
- );
3917
- setResendCooldown(30);
3918
- setView("phone_otp");
3919
- } else {
3920
- await sendOnrampVerificationOtp(
3921
- session.id,
3922
- "email",
3923
- session.client_secret,
3924
- publishableKey
3925
- );
3926
- setResendCooldown(30);
3927
- setView("email_otp");
3928
- }
4036
+ storeApplePaySession();
4037
+ await sendOnrampVerificationOtp(session.id, "phone", session.client_secret, publishableKey);
4038
+ setResendCooldown(30);
4039
+ setView("phone_otp");
3929
4040
  } catch (err) {
3930
4041
  const raw = err instanceof Error ? err.message : "Failed to start verification";
3931
4042
  setErrorMessage(prettifyVerificationError(raw));
3932
4043
  setView("phone_input");
3933
4044
  }
3934
- }, [isContactValid, email, normalizedPhone, publishableKey, persistContact]);
3935
- const submitEmailOtp = useCallback(async () => {
3936
- if (!verificationSession || !clientSecret || emailCode.length !== 6) return;
3937
- if (otpSubmitting) return;
3938
- setOtpError(null);
3939
- setOtpSubmitting(true);
4045
+ }, [email, normalizedPhone, publishableKey, storeApplePaySession]);
4046
+ const clearUpgradeFields = useCallback(() => {
4047
+ setSsnLast4("");
4048
+ setDobInput("");
4049
+ }, []);
4050
+ const { action: initialAction, refetchApplePayLimits } = useApplePayInitialScreen({
4051
+ userId,
4052
+ publishableKey,
4053
+ normalizedPhone,
4054
+ userEmail,
4055
+ sessionPhoneVerified: verificationSession?.phone?.status === "verified",
4056
+ isWaiting: defaultTokenLoading || legalAgreementsLoading || userIpInfoLoading
4057
+ });
4058
+ const pollLimitUpgradeStatus = useCallback(
4059
+ async (signal) => {
4060
+ const POLL_INTERVAL_MS4 = 1500;
4061
+ const POLL_TIMEOUT_MS = 3e4;
4062
+ const startedAt = Date.now();
4063
+ let firstIteration = true;
4064
+ while (Date.now() - startedAt < POLL_TIMEOUT_MS) {
4065
+ if (signal.aborted) return;
4066
+ if (!firstIteration) {
4067
+ await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS4));
4068
+ }
4069
+ firstIteration = false;
4070
+ if (signal.aborted) return;
4071
+ let fresh;
4072
+ try {
4073
+ fresh = await getCoinbaseApplePayLimits2(normalizedPhone, publishableKey, signal);
4074
+ } catch {
4075
+ if (signal.aborted) return;
4076
+ continue;
4077
+ }
4078
+ const { upgrade_status: status, limit_reached: stillCapped } = fresh;
4079
+ if (status === "active" || !stillCapped) {
4080
+ await refetchApplePayLimits();
4081
+ clearUpgradeFields();
4082
+ setLimitUpgradeError(null);
4083
+ if (onrampToken) {
4084
+ setView("amount");
4085
+ } else {
4086
+ if (signal.aborted) return;
4087
+ await createSessionAndSendOtp();
4088
+ }
4089
+ return;
4090
+ }
4091
+ if (status === "inactive") {
4092
+ await refetchApplePayLimits();
4093
+ clearUpgradeFields();
4094
+ setLimitUpgradeError(null);
4095
+ setView("guest_limit_reached");
4096
+ return;
4097
+ }
4098
+ if (status === "resubmit") {
4099
+ await refetchApplePayLimits();
4100
+ setSsnLast4("");
4101
+ setLimitUpgradeError(
4102
+ "We couldn't verify your information. Please check your SSN and date of birth and try again."
4103
+ );
4104
+ setView("limit_upgrade_form");
4105
+ return;
4106
+ }
4107
+ }
4108
+ setLimitUpgradeError(null);
4109
+ setView("limit_upgrade_failed");
4110
+ },
4111
+ [
4112
+ normalizedPhone,
4113
+ publishableKey,
4114
+ refetchApplePayLimits,
4115
+ onrampToken,
4116
+ createSessionAndSendOtp,
4117
+ clearUpgradeFields
4118
+ ]
4119
+ );
4120
+ const startUpgradePolling = useCallback(async () => {
4121
+ upgradeFlowAbortRef.current?.abort();
4122
+ const controller = new AbortController();
4123
+ upgradeFlowAbortRef.current = controller;
3940
4124
  try {
3941
- const updated = await verifyOnrampVerificationOtp(
3942
- verificationSession.id,
3943
- "email",
3944
- clientSecret,
3945
- emailCode,
3946
- publishableKey
3947
- );
3948
- setVerificationSession(updated);
3949
- if (updated.email.status === "verified") {
3950
- persistContact({ emailVerifiedAt: updated.email.verified_at });
3951
- await sendOnrampVerificationOtp(updated.id, "phone", clientSecret, publishableKey);
3952
- setEmailCode("");
3953
- setResendCooldown(30);
3954
- setView("phone_otp");
4125
+ await pollLimitUpgradeStatus(controller.signal);
4126
+ } finally {
4127
+ if (upgradeFlowAbortRef.current === controller) {
4128
+ upgradeFlowAbortRef.current = null;
4129
+ }
4130
+ }
4131
+ }, [pollLimitUpgradeStatus]);
4132
+ const applyLimitRouting = useCallback(
4133
+ (nextView, status) => {
4134
+ if (nextView === "limit_upgrade_form" && status === "resubmit") {
4135
+ setLimitUpgradeError(
4136
+ "We couldn't verify your information. Please check your SSN and date of birth and try again."
4137
+ );
3955
4138
  } else {
3956
- setOtpError("Incorrect code. Please try again.");
3957
- setEmailCode("");
4139
+ setLimitUpgradeError(null);
3958
4140
  }
3959
- } catch (err) {
3960
- setOtpError(
3961
- prettifyVerificationError(err instanceof Error ? err.message : "Verification failed")
3962
- );
3963
- setEmailCode("");
4141
+ setView(nextView);
4142
+ if (nextView === "limit_upgrade_submitting") {
4143
+ void startUpgradePolling();
4144
+ }
4145
+ },
4146
+ [startUpgradePolling]
4147
+ );
4148
+ const hasInitializedRef = useRef3(false);
4149
+ useEffect7(() => {
4150
+ if (hasInitializedRef.current) return;
4151
+ if (initialAction.kind === "pending") return;
4152
+ hasInitializedRef.current = true;
4153
+ switch (initialAction.kind) {
4154
+ case "navigate":
4155
+ setView(initialAction.view);
4156
+ break;
4157
+ case "limit_upgrade":
4158
+ applyLimitRouting(initialAction.view, initialAction.status);
4159
+ break;
4160
+ case "resume_with_token":
4161
+ setOnrampToken(initialAction.token);
4162
+ setView("amount");
4163
+ break;
4164
+ case "resume_send_sms":
4165
+ void createSessionAndSendOtp();
4166
+ break;
4167
+ }
4168
+ }, [initialAction, applyLimitRouting, createSessionAndSendOtp]);
4169
+ const startVerification = useCallback(async () => {
4170
+ if (!isContactValid) return;
4171
+ await createSessionAndSendOtp();
4172
+ }, [isContactValid, createSessionAndSendOtp]);
4173
+ const submitLimitUpgrade = useCallback(async () => {
4174
+ if (upgradeSubmitting) return;
4175
+ try {
4176
+ const dob = parseDobInput(dobInput);
4177
+ if (!dob || !/^\d{4}$/.test(ssnLast4)) return;
4178
+ setUpgradeSubmitting(true);
4179
+ setLimitUpgradeError(null);
4180
+ setView("limit_upgrade_submitting");
4181
+ const requestController = new AbortController();
4182
+ upgradeFlowAbortRef.current?.abort();
4183
+ upgradeFlowAbortRef.current = requestController;
4184
+ try {
4185
+ await requestCoinbaseApplePayLimitUpgrade(
4186
+ {
4187
+ phone: normalizedPhone,
4188
+ fields: { ssnLast4, dateOfBirth: dob }
4189
+ },
4190
+ publishableKey,
4191
+ requestController.signal
4192
+ );
4193
+ } catch (err) {
4194
+ if (requestController.signal.aborted) return;
4195
+ const raw = err instanceof Error ? err.message : "Failed to request limit upgrade";
4196
+ if (isLimitUpgradeTerminalBlock(raw)) {
4197
+ clearUpgradeFields();
4198
+ setLimitUpgradeError(null);
4199
+ setView("guest_limit_reached");
4200
+ return;
4201
+ }
4202
+ setLimitUpgradeError(prettifyVerificationError(raw));
4203
+ setView("limit_upgrade_form");
4204
+ return;
4205
+ } finally {
4206
+ if (upgradeFlowAbortRef.current === requestController) {
4207
+ upgradeFlowAbortRef.current = null;
4208
+ }
4209
+ }
4210
+ await startUpgradePolling();
3964
4211
  } finally {
3965
- setOtpSubmitting(false);
4212
+ setUpgradeSubmitting(false);
3966
4213
  }
3967
4214
  }, [
3968
- verificationSession,
3969
- clientSecret,
3970
- emailCode,
4215
+ upgradeSubmitting,
4216
+ dobInput,
4217
+ ssnLast4,
4218
+ normalizedPhone,
3971
4219
  publishableKey,
3972
- persistContact,
3973
- otpSubmitting
4220
+ startUpgradePolling,
4221
+ clearUpgradeFields
3974
4222
  ]);
3975
4223
  const submitPhoneOtp = useCallback(async () => {
3976
4224
  if (!verificationSession || !clientSecret || phoneCode.length !== 6) return;
@@ -3993,7 +4241,7 @@ var BuyWithApplePay = React5.forwardRef(
3993
4241
  publishableKey
3994
4242
  );
3995
4243
  setOnrampToken(tokenRes);
3996
- persistContact({
4244
+ storeApplePaySession({
3997
4245
  emailVerifiedAt: updated.email.verified_at,
3998
4246
  phoneVerifiedAt: updated.phone.verified_at,
3999
4247
  onrampToken: tokenRes.token,
@@ -4002,17 +4250,30 @@ var BuyWithApplePay = React5.forwardRef(
4002
4250
  setEmailLocked(true);
4003
4251
  setPhoneCode("");
4004
4252
  if (amountUsd) setAmount(amountUsd);
4253
+ try {
4254
+ const result = await refetchApplePayLimits();
4255
+ if (result.data) {
4256
+ const nextView = getViewForLimitStatus(result.data);
4257
+ if (nextView) {
4258
+ setOtpSubmitting(false);
4259
+ applyLimitRouting(nextView, result.data.upgrade_status);
4260
+ return;
4261
+ }
4262
+ }
4263
+ } catch {
4264
+ }
4265
+ setOtpSubmitting(false);
4005
4266
  setView("amount");
4006
- } else {
4007
- setOtpError("Incorrect code. Please try again.");
4008
- setPhoneCode("");
4267
+ return;
4009
4268
  }
4269
+ setOtpError("Incorrect code. Please try again.");
4270
+ setPhoneCode("");
4271
+ setOtpSubmitting(false);
4010
4272
  } catch (err) {
4011
4273
  setOtpError(
4012
4274
  prettifyVerificationError(err instanceof Error ? err.message : "Verification failed")
4013
4275
  );
4014
4276
  setPhoneCode("");
4015
- } finally {
4016
4277
  setOtpSubmitting(false);
4017
4278
  }
4018
4279
  }, [
@@ -4020,22 +4281,25 @@ var BuyWithApplePay = React5.forwardRef(
4020
4281
  clientSecret,
4021
4282
  phoneCode,
4022
4283
  publishableKey,
4023
- persistContact,
4284
+ storeApplePaySession,
4024
4285
  amountUsd,
4025
- otpSubmitting
4286
+ otpSubmitting,
4287
+ refetchApplePayLimits,
4288
+ getViewForLimitStatus,
4289
+ applyLimitRouting
4026
4290
  ]);
4027
4291
  const prepareSession = useCallback(
4028
4292
  async (token, amt) => {
4293
+ setErrorMessage("");
4029
4294
  if (!destinationChainType || !destinationChainId) {
4030
4295
  setErrorMessage("Deposit destination not configured. Please contact support.");
4031
4296
  return;
4032
4297
  }
4033
- const destinationNetwork = destinationChainNameFor(
4034
- destinationChainType,
4035
- destinationChainId
4036
- );
4037
- if (!destinationNetwork) {
4038
- setErrorMessage("Coinbase Apple Pay is not available for this network.");
4298
+ if (defaultTokenLoading) {
4299
+ return;
4300
+ }
4301
+ if (defaultTokenError || !defaultToken) {
4302
+ setErrorMessage("Unable to load token information. Please try again.");
4039
4303
  return;
4040
4304
  }
4041
4305
  const settlementAddress = depositWallet?.address;
@@ -4046,19 +4310,17 @@ var BuyWithApplePay = React5.forwardRef(
4046
4310
  sessionAbortRef.current?.abort();
4047
4311
  const controller = new AbortController();
4048
4312
  sessionAbortRef.current = controller;
4049
- setPreparingSession(true);
4050
- setErrorMessage("");
4051
4313
  try {
4052
4314
  const externalId = generatePrefixedKSUID3("orsext");
4053
4315
  const session = await createCoinbaseApplePaySession(
4054
4316
  {
4055
4317
  source_currency: "usd",
4056
4318
  source_amount: amt,
4057
- destination_currency: destinationTokenSymbol.toLowerCase(),
4058
- destination_network: destinationNetwork,
4319
+ destination_currency: defaultToken.destination_currency,
4320
+ destination_network: defaultToken.destination_network,
4059
4321
  wallet_address: settlementAddress,
4060
4322
  external_id: externalId,
4061
- ...externalUserEmail ? { email: externalUserEmail } : {}
4323
+ ...userEmail ? { email: userEmail } : {}
4062
4324
  },
4063
4325
  token,
4064
4326
  publishableKey,
@@ -4102,18 +4364,16 @@ var BuyWithApplePay = React5.forwardRef(
4102
4364
  setPaymentSession(null);
4103
4365
  setPaymentSessionAmount(null);
4104
4366
  setErrorMessage("Unable to process your request. Please try again later.");
4105
- } finally {
4106
- if (sessionAbortRef.current === controller) {
4107
- setPreparingSession(false);
4108
- }
4109
4367
  }
4110
4368
  },
4111
4369
  [
4112
- destinationTokenSymbol,
4113
4370
  destinationChainType,
4114
4371
  destinationChainId,
4372
+ defaultToken,
4373
+ defaultTokenLoading,
4374
+ defaultTokenError,
4115
4375
  depositWallet?.address,
4116
- externalUserEmail,
4376
+ userEmail,
4117
4377
  publishableKey,
4118
4378
  onEvent,
4119
4379
  userId
@@ -4157,42 +4417,8 @@ var BuyWithApplePay = React5.forwardRef(
4157
4417
  if (view === "loading_config") {
4158
4418
  return /* @__PURE__ */ jsx13("div", { className: "uf-flex uf-items-center uf-justify-center uf-py-16", style: containerStyle, children: /* @__PURE__ */ jsx13(Loader22, { className: "uf-w-6 uf-h-6 uf-animate-spin", style: { color: colors2.primary } }) });
4159
4419
  }
4160
- if (view === "disabled") {
4161
- return /* @__PURE__ */ jsxs11(
4162
- "div",
4163
- {
4164
- className: "uf-flex uf-flex-col uf-items-center uf-justify-center uf-py-12 uf-px-4",
4165
- style: containerStyle,
4166
- children: [
4167
- /* @__PURE__ */ jsx13(
4168
- AlertCircle,
4169
- {
4170
- className: "uf-w-8 uf-h-8 uf-mb-4",
4171
- style: { color: colors2.foregroundMuted }
4172
- }
4173
- ),
4174
- /* @__PURE__ */ jsx13(
4175
- "h3",
4176
- {
4177
- className: "uf-text-base uf-font-medium uf-mb-1",
4178
- style: { color: colors2.foreground, fontFamily: fonts.medium },
4179
- children: "Apple Pay is unavailable"
4180
- }
4181
- ),
4182
- /* @__PURE__ */ jsx13(
4183
- "p",
4184
- {
4185
- className: "uf-text-sm uf-text-center",
4186
- style: { color: colors2.foregroundMuted, fontFamily: fonts.regular },
4187
- children: "Apple Pay is disabled for this project. Try another payment method."
4188
- }
4189
- )
4190
- ]
4191
- }
4192
- );
4193
- }
4194
4420
  if (view === "email_input") {
4195
- const emailValid = EMAIL_REGEX.test(email);
4421
+ const emailValid = EMAIL_REGEX2.test(email);
4196
4422
  return /* @__PURE__ */ jsxs11("div", { className: "uf-flex uf-flex-col uf-pb-1 uf-pt-2", style: containerStyle, children: [
4197
4423
  /* @__PURE__ */ jsx13(
4198
4424
  "label",
@@ -4209,9 +4435,7 @@ var BuyWithApplePay = React5.forwardRef(
4209
4435
  value: email,
4210
4436
  onChange: (e) => setEmail(e.target.value.trim()),
4211
4437
  onKeyDown: (e) => {
4212
- if (e.key === "Enter" && emailValid) {
4213
- setView("phone_input");
4214
- }
4438
+ if (e.key === "Enter" && emailValid) setView("phone_input");
4215
4439
  },
4216
4440
  placeholder: "you@example.com",
4217
4441
  autoComplete: "email",
@@ -4247,7 +4471,7 @@ var BuyWithApplePay = React5.forwardRef(
4247
4471
  }
4248
4472
  if (view === "phone_input" || view === "submitting_session") {
4249
4473
  const submitting = view === "submitting_session";
4250
- const phoneValid = PHONE_REGEX.test(normalizedPhone);
4474
+ const phoneValid = PHONE_REGEX2.test(normalizedPhone);
4251
4475
  const rawDigits = (() => {
4252
4476
  const digits = phoneInput.replace(/\D/g, "");
4253
4477
  return digits.startsWith("1") ? digits.slice(1, 11) : digits.slice(0, 10);
@@ -4328,15 +4552,7 @@ var BuyWithApplePay = React5.forwardRef(
4328
4552
  !phoneInput && /* @__PURE__ */ jsx13("div", { className: "uf-text-xs uf-mb-2 uf-px-1", style: { color: colors2.foregroundMuted }, children: "Re-verified every 60 days." }),
4329
4553
  errorMessage && /* @__PURE__ */ jsx13("div", { className: "uf-text-xs uf-mt-2 uf-px-1", style: { color: colors2.error }, children: errorMessage }),
4330
4554
  /* @__PURE__ */ jsx13("div", { className: "uf-flex-1" }),
4331
- /* @__PURE__ */ jsx13(
4332
- LegalDisclaimer,
4333
- {
4334
- legalAgreements,
4335
- loading: legalAgreementsLoading,
4336
- colors: colors2,
4337
- fonts
4338
- }
4339
- ),
4555
+ /* @__PURE__ */ jsx13(LegalDisclaimer, { legalAgreements, loading: legalAgreementsLoading }),
4340
4556
  /* @__PURE__ */ jsx13(
4341
4557
  "button",
4342
4558
  {
@@ -4355,12 +4571,9 @@ var BuyWithApplePay = React5.forwardRef(
4355
4571
  )
4356
4572
  ] });
4357
4573
  }
4358
- if (view === "email_otp" || view === "phone_otp") {
4359
- const isEmail = view === "email_otp";
4360
- const code = isEmail ? emailCode : phoneCode;
4361
- const setCode = isEmail ? setEmailCode : setPhoneCode;
4362
- const target = isEmail ? maskEmail(email) : maskPhone(normalizedPhone);
4363
- const submit = isEmail ? submitEmailOtp : submitPhoneOtp;
4574
+ if (view === "phone_otp") {
4575
+ const code = phoneCode;
4576
+ const target = maskPhone(normalizedPhone);
4364
4577
  const canSubmit = code.length === 6 && !otpSubmitting;
4365
4578
  return /* @__PURE__ */ jsxs11("div", { className: "uf-flex uf-flex-col uf-pb-1 uf-items-center uf-pt-2", style: containerStyle, children: [
4366
4579
  /* @__PURE__ */ jsxs11(
@@ -4383,13 +4596,12 @@ var BuyWithApplePay = React5.forwardRef(
4383
4596
  maxLength: 6,
4384
4597
  value: code,
4385
4598
  onChange: (e) => {
4386
- const v = e.target.value.replace(/\D/g, "").slice(0, 6);
4387
- setCode(v);
4599
+ setPhoneCode(e.target.value.replace(/\D/g, "").slice(0, 6));
4388
4600
  setOtpError(null);
4389
4601
  },
4390
4602
  onKeyDown: (e) => {
4391
4603
  if (e.key === "Enter" && code.length === 6 && !otpSubmitting) {
4392
- void submit();
4604
+ void submitPhoneOtp();
4393
4605
  }
4394
4606
  },
4395
4607
  autoFocus: true,
@@ -4416,7 +4628,7 @@ var BuyWithApplePay = React5.forwardRef(
4416
4628
  if (!verificationSession || !clientSecret) return;
4417
4629
  void sendOnrampVerificationOtp(
4418
4630
  verificationSession.id,
4419
- isEmail ? "email" : "phone",
4631
+ "phone",
4420
4632
  clientSecret,
4421
4633
  publishableKey
4422
4634
  ).then(() => setResendCooldown(30));
@@ -4429,7 +4641,7 @@ var BuyWithApplePay = React5.forwardRef(
4429
4641
  /* @__PURE__ */ jsx13(
4430
4642
  "button",
4431
4643
  {
4432
- onClick: () => void submit(),
4644
+ onClick: submitPhoneOtp,
4433
4645
  disabled: !canSubmit,
4434
4646
  className: "uf-w-full uf-py-3 uf-mt-6 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",
4435
4647
  style: {
@@ -4577,7 +4789,7 @@ var BuyWithApplePay = React5.forwardRef(
4577
4789
  border: `${components.button.borderWidth}px solid ${components.button.borderColor}`,
4578
4790
  opacity: 0.5
4579
4791
  },
4580
- children: !hasInput || !validAmount ? "Enter amount" : /* @__PURE__ */ jsxs11(Fragment4, { children: [
4792
+ children: !hasInput || !validAmount ? "Enter amount" : errorMessage ? "Unable to prepare Apple Pay" : defaultTokenLoading ? "Loading token information\u2026" : defaultTokenError ? "Token routing unavailable" : /* @__PURE__ */ jsxs11(Fragment4, { children: [
4581
4793
  /* @__PURE__ */ jsx13(Loader22, { className: "uf-w-4 uf-h-4 uf-animate-spin" }),
4582
4794
  "Preparing Apple Pay"
4583
4795
  ] })
@@ -4630,6 +4842,274 @@ var BuyWithApplePay = React5.forwardRef(
4630
4842
  }
4631
4843
  );
4632
4844
  }
4845
+ if (view === "limit_upgrade_intro") {
4846
+ return /* @__PURE__ */ jsxs11("div", { className: "uf-flex uf-flex-col uf-pt-8 uf-pb-2 uf-px-2", style: containerStyle, children: [
4847
+ /* @__PURE__ */ jsx13(
4848
+ "h3",
4849
+ {
4850
+ className: "uf-text-xl uf-mb-3 uf-text-center",
4851
+ style: { color: colors2.foreground, fontFamily: fonts.medium },
4852
+ children: "Apple Pay limit reached"
4853
+ }
4854
+ ),
4855
+ /* @__PURE__ */ jsx13(
4856
+ "p",
4857
+ {
4858
+ className: "uf-text-sm uf-text-center uf-mb-10 uf-max-w-[320px] uf-mx-auto",
4859
+ style: { color: colors2.foregroundMuted, fontFamily: fonts.regular },
4860
+ children: "Submit your date of birth and SSN to increase your Apple Pay limit."
4861
+ }
4862
+ ),
4863
+ /* @__PURE__ */ jsx13(
4864
+ "button",
4865
+ {
4866
+ onClick: () => {
4867
+ setLimitUpgradeError(null);
4868
+ setView("limit_upgrade_form");
4869
+ },
4870
+ className: "uf-w-full uf-py-3 uf-mb-2 uf-text-sm uf-font-medium uf-transition-colors",
4871
+ style: {
4872
+ backgroundColor: colors2.primary,
4873
+ color: colors2.primaryForeground,
4874
+ fontFamily: fonts.medium,
4875
+ borderRadius: components.button.borderRadius,
4876
+ border: `${components.button.borderWidth}px solid ${components.button.borderColor}`
4877
+ },
4878
+ children: "Continue"
4879
+ }
4880
+ ),
4881
+ /* @__PURE__ */ jsx13(
4882
+ "button",
4883
+ {
4884
+ onClick: () => {
4885
+ setLimitUpgradeError(null);
4886
+ onExit?.();
4887
+ },
4888
+ className: "uf-w-full uf-py-3 uf-text-sm uf-font-medium uf-transition-colors",
4889
+ style: {
4890
+ backgroundColor: "transparent",
4891
+ color: colors2.error,
4892
+ fontFamily: fonts.medium,
4893
+ borderRadius: components.button.borderRadius,
4894
+ border: `${components.button.borderWidth}px solid transparent`
4895
+ },
4896
+ children: "Cancel"
4897
+ }
4898
+ )
4899
+ ] });
4900
+ }
4901
+ if (view === "limit_upgrade_submitting") {
4902
+ return /* @__PURE__ */ jsxs11(
4903
+ "div",
4904
+ {
4905
+ className: "uf-flex uf-flex-col uf-items-center uf-justify-center uf-py-16 uf-px-4",
4906
+ style: containerStyle,
4907
+ children: [
4908
+ /* @__PURE__ */ jsx13(
4909
+ "div",
4910
+ {
4911
+ className: "uf-w-20 uf-h-20 uf-rounded-full uf-flex uf-items-center uf-justify-center uf-mb-6",
4912
+ style: { backgroundColor: `${colors2.primary}20` },
4913
+ children: /* @__PURE__ */ jsx13(
4914
+ Loader22,
4915
+ {
4916
+ className: "uf-w-10 uf-h-10 uf-animate-spin",
4917
+ style: { color: colors2.primary }
4918
+ }
4919
+ )
4920
+ }
4921
+ ),
4922
+ /* @__PURE__ */ jsx13(
4923
+ "h3",
4924
+ {
4925
+ className: "uf-text-xl uf-mb-2 uf-text-center",
4926
+ style: { color: colors2.foreground, fontFamily: fonts.medium },
4927
+ children: "Verifying your information"
4928
+ }
4929
+ ),
4930
+ /* @__PURE__ */ jsx13(
4931
+ "p",
4932
+ {
4933
+ className: "uf-text-sm uf-text-center uf-max-w-[280px]",
4934
+ style: { color: colors2.foregroundMuted, fontFamily: fonts.regular },
4935
+ children: "Your limit increase request is under review. This usually takes just a few seconds."
4936
+ }
4937
+ )
4938
+ ]
4939
+ }
4940
+ );
4941
+ }
4942
+ if (view === "limit_upgrade_form") {
4943
+ const dobValid = isDobInputValid(dobInput);
4944
+ const ssnValid = /^\d{4}$/.test(ssnLast4);
4945
+ const formValid = dobValid && ssnValid;
4946
+ const formInputStyle = {
4947
+ backgroundColor: components.card.backgroundColor,
4948
+ color: components.input.textColor,
4949
+ fontFamily: fonts.regular,
4950
+ borderRadius: components.card.borderRadius,
4951
+ border: `${components.card.borderWidth}px solid ${components.card.borderColor}`
4952
+ };
4953
+ return /* @__PURE__ */ jsxs11("div", { className: "uf-flex uf-flex-col uf-pb-1 uf-pt-2", style: containerStyle, children: [
4954
+ /* @__PURE__ */ jsx13(
4955
+ "label",
4956
+ {
4957
+ className: "uf-text-xs uf-font-medium uf-mb-1.5 uf-px-1",
4958
+ style: { color: components.card.labelColor, fontFamily: fonts.medium },
4959
+ children: "Date of birth"
4960
+ }
4961
+ ),
4962
+ /* @__PURE__ */ jsx13(
4963
+ "input",
4964
+ {
4965
+ type: "text",
4966
+ inputMode: "numeric",
4967
+ maxLength: 10,
4968
+ value: dobInput,
4969
+ onChange: (e) => {
4970
+ setDobInput(formatDobInput(e.target.value));
4971
+ if (limitUpgradeError) setLimitUpgradeError(null);
4972
+ },
4973
+ onKeyDown: (e) => {
4974
+ if (e.key === "Enter" && formValid) void submitLimitUpgrade();
4975
+ },
4976
+ placeholder: "MM/DD/YYYY",
4977
+ autoComplete: "bday",
4978
+ "aria-label": "Date of birth",
4979
+ className: "uf-w-full uf-px-3 uf-py-3 uf-mb-3 uf-text-sm uf-outline-none",
4980
+ style: formInputStyle
4981
+ }
4982
+ ),
4983
+ /* @__PURE__ */ jsx13(
4984
+ "label",
4985
+ {
4986
+ className: "uf-text-xs uf-font-medium uf-mb-1.5 uf-px-1",
4987
+ style: { color: components.card.labelColor, fontFamily: fonts.medium },
4988
+ children: "Last 4 of SSN"
4989
+ }
4990
+ ),
4991
+ /* @__PURE__ */ jsx13(
4992
+ "input",
4993
+ {
4994
+ type: "password",
4995
+ inputMode: "numeric",
4996
+ pattern: "[0-9]*",
4997
+ maxLength: 4,
4998
+ value: ssnLast4,
4999
+ onChange: (e) => {
5000
+ setSsnLast4(e.target.value.replace(/\D/g, "").slice(0, 4));
5001
+ if (limitUpgradeError) setLimitUpgradeError(null);
5002
+ },
5003
+ onKeyDown: (e) => {
5004
+ if (e.key === "Enter" && formValid) void submitLimitUpgrade();
5005
+ },
5006
+ placeholder: "\u2022\u2022\u2022\u2022",
5007
+ autoComplete: "off",
5008
+ name: "off",
5009
+ autoCorrect: "off",
5010
+ autoCapitalize: "none",
5011
+ spellCheck: false,
5012
+ "data-1p-ignore": "true",
5013
+ "data-lpignore": "true",
5014
+ "data-form-type": "other",
5015
+ "aria-label": "Last 4 digits of SSN",
5016
+ "aria-autocomplete": "none",
5017
+ className: "uf-w-full uf-px-3 uf-py-3 uf-mb-2 uf-text-sm uf-outline-none",
5018
+ style: formInputStyle
5019
+ }
5020
+ ),
5021
+ limitUpgradeError && /* @__PURE__ */ jsx13(
5022
+ "div",
5023
+ {
5024
+ className: "uf-text-xs uf-mt-2 uf-px-1",
5025
+ style: { color: colors2.error, fontFamily: fonts.regular },
5026
+ children: limitUpgradeError
5027
+ }
5028
+ ),
5029
+ /* @__PURE__ */ jsx13("div", { className: "uf-flex-1" }),
5030
+ /* @__PURE__ */ jsx13(
5031
+ "button",
5032
+ {
5033
+ onClick: () => void submitLimitUpgrade(),
5034
+ disabled: !formValid || upgradeSubmitting,
5035
+ className: "uf-w-full uf-py-3 uf-mt-3 uf-text-sm uf-font-medium uf-transition-colors disabled:uf-opacity-50 disabled:uf-cursor-not-allowed",
5036
+ style: {
5037
+ backgroundColor: colors2.primary,
5038
+ color: colors2.primaryForeground,
5039
+ fontFamily: fonts.medium,
5040
+ borderRadius: components.button.borderRadius,
5041
+ border: `${components.button.borderWidth}px solid ${components.button.borderColor}`
5042
+ },
5043
+ children: upgradeSubmitting ? /* @__PURE__ */ jsxs11("span", { className: "uf-flex uf-items-center uf-justify-center uf-gap-2", children: [
5044
+ /* @__PURE__ */ jsx13(Loader22, { className: "uf-w-4 uf-h-4 uf-animate-spin" }),
5045
+ "Submitting\u2026"
5046
+ ] }) : "Submit"
5047
+ }
5048
+ )
5049
+ ] });
5050
+ }
5051
+ if (view === "limit_upgrade_failed") {
5052
+ return /* @__PURE__ */ jsxs11("div", { className: "uf-flex uf-flex-col uf-pt-8 uf-pb-2 uf-px-2", style: containerStyle, children: [
5053
+ /* @__PURE__ */ jsx13(
5054
+ "h3",
5055
+ {
5056
+ className: "uf-text-xl uf-mb-3 uf-text-center",
5057
+ style: { color: colors2.foreground, fontFamily: fonts.medium },
5058
+ children: "Apple Pay limit reached"
5059
+ }
5060
+ ),
5061
+ /* @__PURE__ */ jsxs11(
5062
+ "p",
5063
+ {
5064
+ className: "uf-text-sm uf-text-center uf-mb-2 uf-max-w-[320px] uf-mx-auto",
5065
+ style: { color: colors2.foregroundMuted, fontFamily: fonts.regular },
5066
+ children: [
5067
+ "Failed to increase. Please try again later or",
5068
+ " ",
5069
+ /* @__PURE__ */ jsx13(
5070
+ "a",
5071
+ {
5072
+ href: UNIFOLD_SUPPORT_URL,
5073
+ target: "_blank",
5074
+ rel: "noreferrer noopener",
5075
+ className: "uf-underline",
5076
+ style: { color: colors2.primary },
5077
+ children: "contact support"
5078
+ }
5079
+ ),
5080
+ "."
5081
+ ]
5082
+ }
5083
+ ),
5084
+ limitUpgradeError && /* @__PURE__ */ jsx13(
5085
+ "p",
5086
+ {
5087
+ className: "uf-text-xs uf-text-center uf-mb-2 uf-px-2",
5088
+ style: { color: colors2.error, fontFamily: fonts.regular },
5089
+ children: limitUpgradeError
5090
+ }
5091
+ ),
5092
+ /* @__PURE__ */ jsx13("div", { className: "uf-mb-8" }),
5093
+ /* @__PURE__ */ jsx13(
5094
+ "button",
5095
+ {
5096
+ onClick: () => {
5097
+ setLimitUpgradeError(null);
5098
+ onExit?.();
5099
+ },
5100
+ className: "uf-w-full uf-py-3 uf-text-sm uf-font-medium uf-transition-colors",
5101
+ style: {
5102
+ backgroundColor: colors2.primary,
5103
+ color: colors2.primaryForeground,
5104
+ fontFamily: fonts.medium,
5105
+ borderRadius: components.button.borderRadius,
5106
+ border: `${components.button.borderWidth}px solid ${components.button.borderColor}`
5107
+ },
5108
+ children: "Return"
5109
+ }
5110
+ )
5111
+ ] });
5112
+ }
4633
5113
  if (view === "guest_limit_reached") {
4634
5114
  return /* @__PURE__ */ jsxs11(
4635
5115
  "div",
@@ -4650,7 +5130,7 @@ var BuyWithApplePay = React5.forwardRef(
4650
5130
  {
4651
5131
  className: "uf-text-sm uf-text-center uf-mb-12 uf-max-w-[300px]",
4652
5132
  style: { color: colors2.foregroundMuted, fontFamily: fonts.regular },
4653
- children: "Your account has reached Apple Pay limit. It's not eligible for an increase right now."
5133
+ children: "Your account is not eligible for a limit increase at this time."
4654
5134
  }
4655
5135
  ),
4656
5136
  /* @__PURE__ */ jsx13(
@@ -4676,7 +5156,95 @@ var BuyWithApplePay = React5.forwardRef(
4676
5156
  }
4677
5157
  );
4678
5158
  BuyWithApplePay.displayName = "BuyWithApplePay";
4679
- function LegalDisclaimer({ legalAgreements, loading, colors: colors2, fonts }) {
5159
+ function formatPhoneInput(raw) {
5160
+ const digits = raw.replace(/\D/g, "");
5161
+ if (raw.startsWith("+")) {
5162
+ return `+${digits}`;
5163
+ }
5164
+ if (digits.length === 10) return `+1${digits}`;
5165
+ if (digits.length === 11 && digits.startsWith("1")) return `+${digits}`;
5166
+ return digits.length > 0 ? `+${digits}` : "";
5167
+ }
5168
+ function maskPhone(phone) {
5169
+ if (!/^\+1\d{10}$/.test(phone)) return phone;
5170
+ return `+1 (***) ***-${phone.slice(-4)}`;
5171
+ }
5172
+ function isGuestLimitError(err) {
5173
+ const message = err instanceof Error ? err.message : String(err ?? "");
5174
+ if (/coinbase_onramp_guest_limit_reached/i.test(message)) return true;
5175
+ if (/guest_(daily_)?transaction_(count|limit)/i.test(message)) return true;
5176
+ return false;
5177
+ }
5178
+ function isLimitUpgradeTerminalBlock(message) {
5179
+ if (/coinbase_onramp_guest_limit_reached/i.test(message)) return true;
5180
+ if (/limit upgrade is not available/i.test(message)) return true;
5181
+ if (/limits.?upgrade.?blocked/i.test(message)) return true;
5182
+ return false;
5183
+ }
5184
+ function prettifyVerificationError(message) {
5185
+ if (/\b429\b|too many requests|rate.?limit|throttle/i.test(message)) {
5186
+ return "Too many attempts. Please wait a few minutes and try again.";
5187
+ }
5188
+ if (/network|failed to fetch|abort/i.test(message)) {
5189
+ return "Network error. Check your connection and try again.";
5190
+ }
5191
+ if (/voip|not a valid US|not a cell/i.test(message)) {
5192
+ return "We can only verify real US cell phones (not VoIP). Try a different number.";
5193
+ }
5194
+ const cleaned = message.replace(/\s*\[[\w_]+\]:\s*.*$/, "").trim();
5195
+ return cleaned || message;
5196
+ }
5197
+ function formatDobInput(raw) {
5198
+ const digits = raw.replace(/\D/g, "").slice(0, 8);
5199
+ if (digits.length <= 2) return digits;
5200
+ if (digits.length <= 4) return `${digits.slice(0, 2)}/${digits.slice(2)}`;
5201
+ return `${digits.slice(0, 2)}/${digits.slice(2, 4)}/${digits.slice(4)}`;
5202
+ }
5203
+ function isDobInputValid(input) {
5204
+ return parseDobInput(input) !== null;
5205
+ }
5206
+ function parseDobInput(input) {
5207
+ const m = /^(\d{2})\/(\d{2})\/(\d{4})$/.exec(input);
5208
+ if (!m) return null;
5209
+ const [, month, day, year] = m;
5210
+ if (!/^(0[1-9]|1[0-2])$/.test(month)) return null;
5211
+ if (!/^(0[1-9]|[12]\d|3[01])$/.test(day)) return null;
5212
+ if (!/^(19|20)\d{2}$/.test(year)) return null;
5213
+ return { month, day, year };
5214
+ }
5215
+ var POPUP_W = 500;
5216
+ var POPUP_H = 700;
5217
+ function openCoinbasePaymentPopup(url) {
5218
+ if (typeof window === "undefined") return null;
5219
+ const screenW = window.screen.availWidth ?? window.innerWidth;
5220
+ const screenH = window.screen.availHeight ?? window.innerHeight;
5221
+ const left = Math.max(0, Math.round((screenW - POPUP_W) / 2));
5222
+ const top = Math.max(0, Math.round((screenH - POPUP_H) / 2));
5223
+ return window.open(
5224
+ url,
5225
+ "unifold_coinbase_apple_pay",
5226
+ `popup,width=${POPUP_W},height=${POPUP_H},left=${left},top=${top}`
5227
+ );
5228
+ }
5229
+ var FALLBACK_AGREEMENTS = [
5230
+ {
5231
+ key: "guest_checkout_terms",
5232
+ name: "Guest Checkout Terms of Service",
5233
+ url: "https://www.coinbase.com/legal/cb-pay/onramp-guest-checkout-terms"
5234
+ },
5235
+ {
5236
+ key: "user_agreement",
5237
+ name: "User Agreement",
5238
+ url: "https://www.coinbase.com/legal/user_agreement"
5239
+ },
5240
+ {
5241
+ key: "privacy_policy",
5242
+ name: "Privacy Policy",
5243
+ url: "https://www.coinbase.com/legal/privacy"
5244
+ }
5245
+ ];
5246
+ function LegalDisclaimer({ legalAgreements, loading }) {
5247
+ const { colors: colors2, fonts } = useTheme();
4680
5248
  if (loading) {
4681
5249
  return /* @__PURE__ */ jsxs11(
4682
5250
  "div",
@@ -4720,78 +5288,6 @@ function LegalDisclaimer({ legalAgreements, loading, colors: colors2, fonts }) {
4720
5288
  }
4721
5289
  );
4722
5290
  }
4723
- function isGuestLimitError(err) {
4724
- const message = err instanceof Error ? err.message : String(err ?? "");
4725
- if (/coinbase_onramp_guest_limit_reached/i.test(message)) return true;
4726
- if (/guest_(daily_)?transaction_(count|limit)/i.test(message)) return true;
4727
- return false;
4728
- }
4729
- function prettifyVerificationError(message) {
4730
- if (/\b429\b|too many requests|rate.?limit|throttle/i.test(message)) {
4731
- return "Too many attempts. Please wait a few minutes and try again.";
4732
- }
4733
- if (/network|failed to fetch|abort/i.test(message)) {
4734
- return "Network error. Check your connection and try again.";
4735
- }
4736
- if (/voip|not a valid US|not a cell/i.test(message)) {
4737
- return "We can only verify real US cell phones (not VoIP). Try a different number.";
4738
- }
4739
- return message;
4740
- }
4741
- var FALLBACK_AGREEMENTS = [
4742
- {
4743
- key: "guest_checkout_terms",
4744
- name: "Guest Checkout Terms of Service",
4745
- url: "https://www.coinbase.com/legal/cb-pay/onramp-guest-checkout-terms"
4746
- },
4747
- {
4748
- key: "user_agreement",
4749
- name: "User Agreement",
4750
- url: "https://www.coinbase.com/legal/user_agreement"
4751
- },
4752
- {
4753
- key: "privacy_policy",
4754
- name: "Privacy Policy",
4755
- url: "https://www.coinbase.com/legal/privacy"
4756
- }
4757
- ];
4758
- var POPUP_W = 500;
4759
- var POPUP_H = 700;
4760
- function openCoinbasePaymentPopup(url) {
4761
- if (typeof window === "undefined") return null;
4762
- const screenW = window.screen.availWidth ?? window.innerWidth;
4763
- const screenH = window.screen.availHeight ?? window.innerHeight;
4764
- const left = Math.max(0, Math.round((screenW - POPUP_W) / 2));
4765
- const top = Math.max(0, Math.round((screenH - POPUP_H) / 2));
4766
- return window.open(
4767
- url,
4768
- "unifold_coinbase_apple_pay",
4769
- `popup,width=${POPUP_W},height=${POPUP_H},left=${left},top=${top}`
4770
- );
4771
- }
4772
- function destinationChainNameFor(chainType, chainId) {
4773
- if (chainType === "solana") return "solana";
4774
- if (chainType === "bitcoin") return "bitcoin";
4775
- if (chainType === "ethereum") {
4776
- switch (chainId) {
4777
- case "1":
4778
- return "ethereum";
4779
- case "8453":
4780
- return "base";
4781
- case "137":
4782
- return "polygon";
4783
- case "43114":
4784
- return "avalanche";
4785
- case "42161":
4786
- return "arbitrum";
4787
- case "10":
4788
- return "optimism";
4789
- default:
4790
- return null;
4791
- }
4792
- }
4793
- return null;
4794
- }
4795
5291
 
4796
5292
  // src/components/deposits/PayWithExchange.tsx
4797
5293
  import { useState as useState12 } from "react";
@@ -5059,7 +5555,7 @@ import {
5059
5555
  } from "@unifold/core";
5060
5556
 
5061
5557
  // src/components/deposits/StyledQRCode.tsx
5062
- import { useEffect as useEffect8, useRef as useRef4, useState as useState13, useMemo as useMemo3 } from "react";
5558
+ import { useEffect as useEffect8, useRef as useRef4, useState as useState13, useMemo as useMemo4 } from "react";
5063
5559
  import QRCodeStyling from "qr-code-styling";
5064
5560
  import { jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
5065
5561
  function createQRConfig(value, size, imageUrl, imageSize, darkMode) {
@@ -5112,7 +5608,7 @@ function QRCodeSkeleton({ size = 180, darkMode = false }) {
5112
5608
  const spacing = size / gridCount;
5113
5609
  const fillColor = darkMode ? "rgba(255,255,255,0.10)" : "rgba(0,0,0,0.08)";
5114
5610
  const cornerColor = darkMode ? "rgba(255,255,255,0.14)" : "rgba(0,0,0,0.12)";
5115
- const dots = useMemo3(() => {
5611
+ const dots = useMemo4(() => {
5116
5612
  const result = [];
5117
5613
  const cornerSize = 7;
5118
5614
  const centerStart = Math.floor(gridCount / 2) - 2;
@@ -5216,10 +5712,10 @@ function useIsMobileViewport() {
5216
5712
  }
5217
5713
 
5218
5714
  // src/hooks/use-cashapp-limits.ts
5219
- import { useQuery as useQuery5 } from "@tanstack/react-query";
5715
+ import { useQuery as useQuery6 } from "@tanstack/react-query";
5220
5716
  import { getCashAppLimits } from "@unifold/core";
5221
5717
  function useCashAppLimits({ publishableKey, currency = "usd" }) {
5222
- return useQuery5({
5718
+ return useQuery6({
5223
5719
  queryKey: ["unifold", "cashAppLimits", currency, publishableKey],
5224
5720
  queryFn: () => getCashAppLimits(currency, publishableKey),
5225
5721
  enabled: !!publishableKey,
@@ -5737,7 +6233,7 @@ function PayWithCashApp({
5737
6233
  }
5738
6234
 
5739
6235
  // src/components/deposits/BankTransfer.tsx
5740
- import { useEffect as useEffect11, useMemo as useMemo4, useState as useState16 } from "react";
6236
+ import { useEffect as useEffect11, useMemo as useMemo5, useState as useState16 } from "react";
5741
6237
  import { ChevronRight as ChevronRight3, ExternalLink as ExternalLink3, Landmark } from "lucide-react";
5742
6238
  import {
5743
6239
  DepositEventType as DepositEventType5,
@@ -5747,47 +6243,6 @@ import {
5747
6243
  generatePrefixedKSUID as generatePrefixedKSUID5
5748
6244
  } from "@unifold/core";
5749
6245
 
5750
- // src/hooks/use-default-onramp-token.ts
5751
- import { useQuery as useQuery6 } from "@tanstack/react-query";
5752
- import { getDefaultOnrampToken as getDefaultOnrampToken2 } from "@unifold/core";
5753
- function useDefaultOnrampToken({
5754
- publishableKey,
5755
- tokenAddress,
5756
- chainId,
5757
- chainType,
5758
- countryCode,
5759
- subdivisionCode,
5760
- enabled = true
5761
- }) {
5762
- const { data: defaultToken, isLoading } = useQuery6({
5763
- queryKey: [
5764
- "unifold",
5765
- "defaultOnrampToken",
5766
- publishableKey,
5767
- tokenAddress,
5768
- chainId,
5769
- chainType,
5770
- countryCode,
5771
- subdivisionCode
5772
- ],
5773
- queryFn: () => getDefaultOnrampToken2(
5774
- {
5775
- token_address: tokenAddress,
5776
- chain_id: chainId,
5777
- chain_type: chainType,
5778
- country_code: countryCode,
5779
- subdivision_code: subdivisionCode
5780
- },
5781
- publishableKey
5782
- ),
5783
- enabled: enabled && !!tokenAddress && !!chainId && !!chainType,
5784
- staleTime: 1e3 * 60 * 60,
5785
- refetchOnMount: false,
5786
- refetchOnWindowFocus: false
5787
- });
5788
- return { defaultToken, isLoading };
5789
- }
5790
-
5791
6246
  // src/hooks/use-bank-transfer-providers.ts
5792
6247
  import { useQuery as useQuery7 } from "@tanstack/react-query";
5793
6248
  import { getBankTransferProviders } from "@unifold/core";
@@ -5872,7 +6327,7 @@ function BankTransfer({
5872
6327
  countryCode: userIpInfo?.alpha2
5873
6328
  });
5874
6329
  const providers = providersResponse?.data ?? [];
5875
- const pollingWalletId = useMemo4(() => {
6330
+ const pollingWalletId = useMemo5(() => {
5876
6331
  if (!defaultToken) return void 0;
5877
6332
  return getWalletByChainType3(
5878
6333
  wallets,
@@ -5893,7 +6348,7 @@ function BankTransfer({
5893
6348
  const currencySymbol = getCurrencySymbol2(sourceCurrency);
5894
6349
  const parsedAmount = parseFloat(amount);
5895
6350
  const amountValid = !!amount && Number.isFinite(parsedAmount) && parsedAmount >= MIN_AMOUNT;
5896
- const displayTokenSymbol = useMemo4(
6351
+ const displayTokenSymbol = useMemo5(
5897
6352
  () => destinationTokenSymbol?.toUpperCase() ?? defaultToken?.destination_token_metadata?.symbol?.toUpperCase() ?? defaultToken?.destination_currency?.toUpperCase() ?? "USDC",
5898
6353
  [destinationTokenSymbol, defaultToken]
5899
6354
  );
@@ -9965,50 +10420,116 @@ function VisaIcon({ size = 24 }) {
9965
10420
  /* @__PURE__ */ jsx43(
9966
10421
  "path",
9967
10422
  {
9968
- fill: "#FFF",
9969
- d: "M524.307,142.687c-10.57-3.966-27.135-8.222-47.822-8.222c-52.725,0-89.863,26.551-90.18,64.604c-0.297,28.129,26.514,43.821,46.754,53.185c20.77,9.597,27.752,15.716,27.652,24.283c-0.133,13.123-16.586,19.116-31.924,19.116c-21.355,0-32.701-2.967-50.225-10.274l-6.877-3.112l-7.488,43.823c12.463,5.466,35.508,10.199,59.438,10.445c56.09,0,92.502-26.248,92.916-66.884c0.199-22.27-14.016-39.216-44.801-53.188c-18.65-9.056-30.072-15.099-29.951-24.269c0-8.137,9.668-16.838,30.559-16.838c17.447-0.271,30.088,3.534,39.936,7.5l4.781,2.259L524.307,142.687"
10423
+ fill: "#FFF",
10424
+ d: "M524.307,142.687c-10.57-3.966-27.135-8.222-47.822-8.222c-52.725,0-89.863,26.551-90.18,64.604c-0.297,28.129,26.514,43.821,46.754,53.185c20.77,9.597,27.752,15.716,27.652,24.283c-0.133,13.123-16.586,19.116-31.924,19.116c-21.355,0-32.701-2.967-50.225-10.274l-6.877-3.112l-7.488,43.823c12.463,5.466,35.508,10.199,59.438,10.445c56.09,0,92.502-26.248,92.916-66.884c0.199-22.27-14.016-39.216-44.801-53.188c-18.65-9.056-30.072-15.099-29.951-24.269c0-8.137,9.668-16.838,30.559-16.838c17.447-0.271,30.088,3.534,39.936,7.5l4.781,2.259L524.307,142.687"
10425
+ }
10426
+ ),
10427
+ /* @__PURE__ */ jsx43(
10428
+ "path",
10429
+ {
10430
+ fill: "#FFF",
10431
+ d: "M661.615,138.464h-41.23c-12.773,0-22.332,3.486-27.941,16.234l-79.244,179.402h56.031c0,0,9.16-24.121,11.232-29.418c6.123,0,60.555,0.084,68.336,0.084c1.596,6.854,6.492,29.334,6.492,29.334h49.512L661.615,138.464z M596.198,264.872c4.414-11.279,21.26-54.724,21.26-54.724c-0.314,0.521,4.381-11.334,7.074-18.684l3.607,16.878c0,0,10.217,46.729,12.352,56.527h-44.293V264.872z"
10432
+ }
10433
+ ),
10434
+ /* @__PURE__ */ jsx43(
10435
+ "path",
10436
+ {
10437
+ fill: "#FFF",
10438
+ d: "M45.879,138.465l-0.682,4.073c21.093,5.106,39.932,12.496,56.426,21.689l47.346,169.688h56.455l83.004-195.45h-56.526l-52.238,133.496l-5.564-27.131c-0.261-0.838-0.544-1.678-0.834-2.52L156.106,154.992c-3.229-12.396-12.597-16.095-24.186-16.528H45.879z"
10439
+ }
10440
+ )
10441
+ ] });
10442
+ }
10443
+ function MastercardIcon({ size = 24 }) {
10444
+ return /* @__PURE__ */ jsxs40("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", children: [
10445
+ /* @__PURE__ */ jsx43("circle", { cx: "7", cy: "12", r: "7", fill: "#EA001B" }),
10446
+ /* @__PURE__ */ jsx43("circle", { cx: "17", cy: "12", r: "7", fill: "#FFA200", fillOpacity: 0.8 })
10447
+ ] });
10448
+ }
10449
+ function AmexIcon({ size = 24 }) {
10450
+ const h = size * 0.63;
10451
+ return /* @__PURE__ */ jsxs40("svg", { width: size, height: h, viewBox: "0 0 750 471", fill: "none", children: [
10452
+ /* @__PURE__ */ jsx43("rect", { width: "750", height: "471", rx: "40", fill: "#2557D6" }),
10453
+ /* @__PURE__ */ jsx43(
10454
+ "path",
10455
+ {
10456
+ d: "M0 221.73l36.57-85.34h65.86l20.74 48.5 20.13-48.5h66.17L246.04 221.73v-85.34h72.85l16.45 44.93 16.76-44.93h71.62v85.34h-44.24v-62.22l-20.43 62.22h-39.25l-20.43-62.22v62.22h-87.22l-10.68-25.17h-56.71l-10.68 25.17H82.69l49.06-114.83h-46.9l-48.77 114.83H0zm168.81-42.17l-20.13-47.29-20.13 47.29h40.26z",
10457
+ fill: "#FFF",
10458
+ transform: "translate(135,100) scale(0.65)"
10459
+ }
10460
+ ),
10461
+ /* @__PURE__ */ jsx43(
10462
+ "path",
10463
+ {
10464
+ d: "M365.43 221.73v-85.34h112.36l22.87 27.26 23.48-27.26h54.87l-50.28 42.67 50.28 42.67h-56.1l-22.87-27.56-23.79 27.56H365.43zm44.24-17h53.03l24.7-25.47 24.09 25.47h18.9l-33.55-28.47 33.55-28.87h-17.68l-25.31 25.78-24.4-25.78h-53.33v57z",
10465
+ fill: "#FFF",
10466
+ transform: "translate(135,100) scale(0.65)"
10467
+ }
10468
+ )
10469
+ ] });
10470
+ }
10471
+ function ApplePayIcon({ size = 24, color = "#000" }) {
10472
+ const h = size * 0.62;
10473
+ const w = h * (512 / 210.2);
10474
+ return /* @__PURE__ */ jsx43("svg", { width: w, height: h, viewBox: "0 0 512 210.2", fill: "none", children: /* @__PURE__ */ jsx43(
10475
+ "path",
10476
+ {
10477
+ fill: color,
10478
+ d: "M93.6,27.1C87.6,34.2,78,39.8,68.4,39c-1.2-9.6,3.5-19.8,9-26.1c6-7.3,16.5-12.5,25-12.9 C103.4,10,99.5,19.8,93.6,27.1 M102.3,40.9c-13.9-0.8-25.8,7.9-32.4,7.9c-6.7,0-16.8-7.5-27.8-7.3c-14.3,0.2-27.6,8.3-34.9,21.2 c-15,25.8-3.9,64,10.6,85c7.1,10.4,15.6,21.8,26.8,21.4c10.6-0.4,14.8-6.9,27.6-6.9c12.9,0,16.6,6.9,27.8,6.7 c11.6-0.2,18.9-10.4,26-20.8c8.1-11.8,11.4-23.3,11.6-23.9c-0.2-0.2-22.4-8.7-22.6-34.3c-0.2-21.4,17.5-31.6,18.3-32.2 C123.3,42.9,107.7,41.3,102.3,40.9 M182.6,11.9v155.9h24.2v-53.3h33.5c30.6,0,52.1-21,52.1-51.4c0-30.4-21.1-51.2-51.3-51.2H182.6z M206.8,32.3h27.9c21,0,33,11.2,33,30.9c0,19.7-12,31-33.1,31h-27.8V32.3z M336.6,169c15.2,0,29.3-7.7,35.7-19.9h0.5v18.7h22.4V90.2 c0-22.5-18-37-45.7-37c-25.7,0-44.7,14.7-45.4,34.9h21.8c1.8-9.6,10.7-15.9,22.9-15.9c14.8,0,23.1,6.9,23.1,19.6v8.6l-30.2,1.8 c-28.1,1.7-43.3,13.2-43.3,33.2C298.4,155.6,314.1,169,336.6,169z M343.1,150.5c-12.9,0-21.1-6.2-21.1-15.7c0-9.8,7.9-15.5,23-16.4 l26.9-1.7v8.8C371.9,140.1,359.5,150.5,343.1,150.5z M425.1,210.2c23.6,0,34.7-9,44.4-36.3L512,54.7h-24.6l-28.5,92.1h-0.5 l-28.5-92.1h-25.3l41,113.5l-2.2,6.9c-3.7,11.7-9.7,16.2-20.4,16.2c-1.9,0-5.6-0.2-7.1-0.4v18.7C417.3,210,423.3,210.2,425.1,210.2z"
10479
+ }
10480
+ ) });
10481
+ }
10482
+ function GooglePayIcon({ size = 24, color = "#202124" }) {
10483
+ const h = size * 0.62;
10484
+ const w = h * (80 / 38.1);
10485
+ return /* @__PURE__ */ jsxs40("svg", { width: w, height: h, viewBox: "0 0 80 38.1", fill: "none", children: [
10486
+ /* @__PURE__ */ jsx43(
10487
+ "path",
10488
+ {
10489
+ fill: color,
10490
+ d: "M37.8,19.7V29h-3V6h7.8c1.9,0,3.7,0.7,5.1,2c1.4,1.2,2.1,3,2.1,4.9c0,1.9-0.7,3.6-2.1,4.9c-1.4,1.3-3.1,2-5.1,2 L37.8,19.7L37.8,19.7z M37.8,8.8v8h5c1.1,0,2.2-0.4,2.9-1.2c1.6-1.5,1.6-4,0.1-5.5c0,0-0.1-0.1-0.1-0.1c-0.8-0.8-1.8-1.3-2.9-1.2 L37.8,8.8L37.8,8.8z"
9970
10491
  }
9971
10492
  ),
9972
10493
  /* @__PURE__ */ jsx43(
9973
10494
  "path",
9974
10495
  {
9975
- fill: "#FFF",
9976
- d: "M661.615,138.464h-41.23c-12.773,0-22.332,3.486-27.941,16.234l-79.244,179.402h56.031c0,0,9.16-24.121,11.232-29.418c6.123,0,60.555,0.084,68.336,0.084c1.596,6.854,6.492,29.334,6.492,29.334h49.512L661.615,138.464z M596.198,264.872c4.414-11.279,21.26-54.724,21.26-54.724c-0.314,0.521,4.381-11.334,7.074-18.684l3.607,16.878c0,0,10.217,46.729,12.352,56.527h-44.293V264.872z"
10496
+ fill: color,
10497
+ d: "M56.7,12.8c2.2,0,3.9,0.6,5.2,1.8s1.9,2.8,1.9,4.8V29H61v-2.2h-0.1c-1.2,1.8-2.9,2.7-4.9,2.7 c-1.7,0-3.2-0.5-4.4-1.5c-1.1-1-1.8-2.4-1.8-3.9c0-1.6,0.6-2.9,1.8-3.9c1.2-1,2.9-1.4,4.9-1.4c1.8,0,3.2,0.3,4.3,1v-0.7 c0-1-0.4-2-1.2-2.6c-0.8-0.7-1.8-1.1-2.9-1.1c-1.7,0-3,0.7-3.9,2.1l-2.6-1.6C51.8,13.8,53.9,12.8,56.7,12.8z M52.9,24.2 c0,0.8,0.4,1.5,1,1.9c0.7,0.5,1.5,0.8,2.3,0.8c1.2,0,2.4-0.5,3.3-1.4c1-0.9,1.5-2,1.5-3.2c-0.9-0.7-2.2-1.1-3.9-1.1 c-1.2,0-2.2,0.3-3,0.9C53.3,22.6,52.9,23.3,52.9,24.2z"
9977
10498
  }
9978
10499
  ),
9979
10500
  /* @__PURE__ */ jsx43(
9980
10501
  "path",
9981
10502
  {
9982
- fill: "#FFF",
9983
- d: "M45.879,138.465l-0.682,4.073c21.093,5.106,39.932,12.496,56.426,21.689l47.346,169.688h56.455l83.004-195.45h-56.526l-52.238,133.496l-5.564-27.131c-0.261-0.838-0.544-1.678-0.834-2.52L156.106,154.992c-3.229-12.396-12.597-16.095-24.186-16.528H45.879z"
10503
+ fill: color,
10504
+ d: "M80,13.3l-9.9,22.7h-3l3.7-7.9l-6.5-14.7h3.2l4.7,11.3h0.1l4.6-11.3H80z"
9984
10505
  }
9985
- )
9986
- ] });
9987
- }
9988
- function MastercardIcon({ size = 24 }) {
9989
- return /* @__PURE__ */ jsxs40("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", children: [
9990
- /* @__PURE__ */ jsx43("circle", { cx: "7", cy: "12", r: "7", fill: "#EA001B" }),
9991
- /* @__PURE__ */ jsx43("circle", { cx: "17", cy: "12", r: "7", fill: "#FFA200", fillOpacity: 0.8 })
9992
- ] });
9993
- }
9994
- function AmexIcon({ size = 24 }) {
9995
- const h = size * 0.63;
9996
- return /* @__PURE__ */ jsxs40("svg", { width: size, height: h, viewBox: "0 0 750 471", fill: "none", children: [
9997
- /* @__PURE__ */ jsx43("rect", { width: "750", height: "471", rx: "40", fill: "#2557D6" }),
10506
+ ),
9998
10507
  /* @__PURE__ */ jsx43(
9999
10508
  "path",
10000
10509
  {
10001
- d: "M0 221.73l36.57-85.34h65.86l20.74 48.5 20.13-48.5h66.17L246.04 221.73v-85.34h72.85l16.45 44.93 16.76-44.93h71.62v85.34h-44.24v-62.22l-20.43 62.22h-39.25l-20.43-62.22v62.22h-87.22l-10.68-25.17h-56.71l-10.68 25.17H82.69l49.06-114.83h-46.9l-48.77 114.83H0zm168.81-42.17l-20.13-47.29-20.13 47.29h40.26z",
10002
- fill: "#FFF",
10003
- transform: "translate(135,100) scale(0.65)"
10510
+ fill: "#4285F4",
10511
+ d: "M25.9,17.7c0-0.9-0.1-1.8-0.2-2.7H13.2v5.1h7.1c-0.3,1.6-1.2,3.1-2.6,4v3.3H22C24.5,25.1,25.9,21.7,25.9,17.7z"
10004
10512
  }
10005
10513
  ),
10006
10514
  /* @__PURE__ */ jsx43(
10007
10515
  "path",
10008
10516
  {
10009
- d: "M365.43 221.73v-85.34h112.36l22.87 27.26 23.48-27.26h54.87l-50.28 42.67 50.28 42.67h-56.1l-22.87-27.56-23.79 27.56H365.43zm44.24-17h53.03l24.7-25.47 24.09 25.47h18.9l-33.55-28.47 33.55-28.87h-17.68l-25.31 25.78-24.4-25.78h-53.33v57z",
10010
- fill: "#FFF",
10011
- transform: "translate(135,100) scale(0.65)"
10517
+ fill: "#34A853",
10518
+ d: "M13.2,30.6c3.6,0,6.6-1.2,8.8-3.2l-4.3-3.3c-1.2,0.8-2.7,1.3-4.5,1.3c-3.4,0-6.4-2.3-7.4-5.5H1.4v3.4 C3.7,27.8,8.2,30.6,13.2,30.6z"
10519
+ }
10520
+ ),
10521
+ /* @__PURE__ */ jsx43(
10522
+ "path",
10523
+ {
10524
+ fill: "#FBBC04",
10525
+ d: "M5.8,19.9c-0.6-1.6-0.6-3.4,0-5.1v-3.4H1.4c-1.9,3.7-1.9,8.1,0,11.9L5.8,19.9z"
10526
+ }
10527
+ ),
10528
+ /* @__PURE__ */ jsx43(
10529
+ "path",
10530
+ {
10531
+ fill: "#EA4335",
10532
+ d: "M13.2,9.4c1.9,0,3.7,0.7,5.1,2l0,0l3.8-3.8c-2.4-2.2-5.6-3.5-8.8-3.4c-5,0-9.6,2.8-11.8,7.3l4.4,3.4 C6.8,11.7,9.8,9.4,13.2,9.4z"
10012
10533
  }
10013
10534
  )
10014
10535
  ] });
@@ -10016,13 +10537,45 @@ function AmexIcon({ size = 24 }) {
10016
10537
  function GenericCardIcon({ size = 24, color = "#888" }) {
10017
10538
  return /* @__PURE__ */ jsx43(CreditCard2, { width: size * 0.8, height: size * 0.8, style: { color }, strokeWidth: 1.5 });
10018
10539
  }
10540
+ function parseColor(input) {
10541
+ if (!input) return null;
10542
+ const s = input.trim();
10543
+ if (s.startsWith("#")) {
10544
+ let hex = s.slice(1);
10545
+ if (hex.length === 3)
10546
+ hex = hex.split("").map((c) => c + c).join("");
10547
+ if (hex.length !== 6) return null;
10548
+ const n = parseInt(hex, 16);
10549
+ if (Number.isNaN(n)) return null;
10550
+ return [n >> 16 & 255, n >> 8 & 255, n & 255];
10551
+ }
10552
+ const m = s.match(/rgba?\(([^)]+)\)/i);
10553
+ if (m) {
10554
+ const parts = m[1].split(",").map((p) => parseFloat(p));
10555
+ if (parts.length >= 3 && parts.every((p) => !Number.isNaN(p)))
10556
+ return [parts[0], parts[1], parts[2]];
10557
+ }
10558
+ return null;
10559
+ }
10560
+ function monochromeOn(background) {
10561
+ const rgb = parseColor(background);
10562
+ if (!rgb) return "#000000";
10563
+ const [r, g, b] = rgb;
10564
+ const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255;
10565
+ return luminance > 0.6 ? "#000000" : "#FFFFFF";
10566
+ }
10019
10567
  function CardBrandIcon({
10020
10568
  brand,
10021
10569
  size = 24,
10022
- fallbackColor = "#888"
10570
+ fallbackColor = "#888",
10571
+ monochromeColor = "#000000"
10023
10572
  }) {
10024
10573
  if (!brand) return /* @__PURE__ */ jsx43(GenericCardIcon, { size, color: fallbackColor });
10025
10574
  const lower = brand.toLowerCase();
10575
+ if (lower.includes("apple_pay") || lower.includes("apple pay") || lower === "applepay")
10576
+ return /* @__PURE__ */ jsx43(ApplePayIcon, { size, color: monochromeColor });
10577
+ if (lower.includes("google_pay") || lower.includes("google pay") || lower === "googlepay")
10578
+ return /* @__PURE__ */ jsx43(GooglePayIcon, { size, color: monochromeColor === "#FFFFFF" ? "#FFFFFF" : "#202124" });
10026
10579
  if (lower.includes("visa")) return /* @__PURE__ */ jsx43(VisaIcon, { size });
10027
10580
  if (lower.includes("mastercard") || lower.includes("master"))
10028
10581
  return /* @__PURE__ */ jsx43(MastercardIcon, { size });
@@ -10031,17 +10584,28 @@ function CardBrandIcon({
10031
10584
  }
10032
10585
  function formatPaymentToken(token) {
10033
10586
  if (token.card) {
10034
- const brand = token.card.brand ?? "Card";
10587
+ const cardBrand = token.card.brand ?? "Card";
10588
+ const last4 = token.card.last4;
10589
+ const walletType = token.card.wallet?.type;
10590
+ if (walletType === "apple_pay" || walletType === "google_pay") {
10591
+ const funding = token.card.funding ? capitalize(token.card.funding) : null;
10592
+ return {
10593
+ label: last4 ? `${capitalize(cardBrand)} \u2022\u2022\u2022\u2022 ${last4}` : capitalize(cardBrand),
10594
+ sublabel: funding ?? "",
10595
+ brand: walletType
10596
+ };
10597
+ }
10035
10598
  return {
10036
- label: `${capitalize(brand)} \u2022\u2022\u2022\u2022 ${token.card.last4}`,
10599
+ label: `${capitalize(cardBrand)} \u2022\u2022\u2022\u2022 ${last4}`,
10037
10600
  sublabel: `${capitalize(token.card.funding ?? "card")} \xB7 Expires ${token.card.exp_month}/${token.card.exp_year}`,
10038
- brand
10601
+ brand: cardBrand
10039
10602
  };
10040
10603
  }
10041
10604
  if (token.us_bank_account) {
10605
+ const accountType = token.us_bank_account.account_type;
10042
10606
  return {
10043
10607
  label: `${token.us_bank_account.bank_name ?? "Bank"} \u2022\u2022\u2022\u2022 ${token.us_bank_account.last4}`,
10044
- sublabel: "Bank account"
10608
+ sublabel: accountType ? `${capitalize(accountType)} account` : "Bank account"
10045
10609
  };
10046
10610
  }
10047
10611
  return { label: "Payment method", sublabel: token.type };
@@ -10072,59 +10636,82 @@ import {
10072
10636
 
10073
10637
  // src/components/deposits/stripe-link/use-stripe-onramp.ts
10074
10638
  import { useState as useState28, useEffect as useEffect23, useRef as useRef7, useCallback as useCallback3 } from "react";
10075
- function useStripeOnramp(stripePublishableKey) {
10639
+ function useStripeOnramp(stripePublishableKey, isDark = false) {
10076
10640
  const [coordinator, setCoordinator] = useState28(null);
10077
10641
  const [isLoading, setIsLoading] = useState28(false);
10078
10642
  const [error, setError] = useState28(null);
10079
10643
  const coordinatorRef = useRef7(null);
10080
- const initialize = useCallback3(async () => {
10081
- if (coordinatorRef.current) return coordinatorRef.current;
10082
- if (!stripePublishableKey) return null;
10083
- setIsLoading(true);
10084
- setError(null);
10085
- try {
10086
- console.log("[PayWithStripeLink] Importing @stripe/crypto...");
10087
- const { loadCryptoOnrampAndInitialize } = await import(
10088
- /* @vite-ignore */
10089
- "@stripe/crypto"
10090
- );
10091
- console.log(
10092
- "[PayWithStripeLink] @stripe/crypto imported, initializing with key:",
10093
- stripePublishableKey?.slice(0, 12) + "..."
10094
- );
10095
- const initPromise = loadCryptoOnrampAndInitialize(stripePublishableKey, { theme: "stripe" });
10096
- const timeoutPromise = new Promise(
10097
- (_, reject) => setTimeout(
10098
- () => reject(
10099
- new Error(
10100
- "Stripe SDK initialization timed out after 15 seconds. Check that your Stripe account has the Crypto Onramp feature enabled."
10101
- )
10102
- ),
10103
- 15e3
10104
- )
10105
- );
10106
- const instance = await Promise.race([initPromise, timeoutPromise]);
10107
- if (!instance) {
10108
- throw new Error("Stripe SDK returned null.");
10644
+ const coordinatorThemeRef = useRef7(null);
10645
+ const initPromiseRef = useRef7(null);
10646
+ const isDarkRef = useRef7(isDark);
10647
+ isDarkRef.current = isDark;
10648
+ const initialize = useCallback3(() => {
10649
+ if (coordinatorRef.current) return Promise.resolve(coordinatorRef.current);
10650
+ if (initPromiseRef.current) return initPromiseRef.current;
10651
+ if (!stripePublishableKey) return Promise.resolve(null);
10652
+ const theme = isDarkRef.current;
10653
+ const promise = (async () => {
10654
+ setIsLoading(true);
10655
+ setError(null);
10656
+ try {
10657
+ console.log("[PayWithStripeLink] Importing @stripe/crypto...");
10658
+ const { loadCryptoOnrampAndInitialize } = await import(
10659
+ /* @vite-ignore */
10660
+ "@stripe/crypto"
10661
+ );
10662
+ console.log(
10663
+ "[PayWithStripeLink] @stripe/crypto imported, initializing with key:",
10664
+ stripePublishableKey?.slice(0, 12) + "..."
10665
+ );
10666
+ const initPromise = loadCryptoOnrampAndInitialize(stripePublishableKey, {
10667
+ theme: theme ? "night" : "stripe"
10668
+ });
10669
+ const timeoutPromise = new Promise(
10670
+ (_, reject) => setTimeout(
10671
+ () => reject(
10672
+ new Error(
10673
+ "Stripe SDK initialization timed out after 15 seconds. Check that your Stripe account has the Crypto Onramp feature enabled."
10674
+ )
10675
+ ),
10676
+ 15e3
10677
+ )
10678
+ );
10679
+ const instance = await Promise.race([initPromise, timeoutPromise]);
10680
+ if (!instance) {
10681
+ throw new Error("Stripe SDK returned null.");
10682
+ }
10683
+ console.log("[PayWithStripeLink] Stripe SDK initialized successfully");
10684
+ coordinatorRef.current = instance;
10685
+ coordinatorThemeRef.current = theme;
10686
+ setCoordinator(instance);
10687
+ return coordinatorRef.current;
10688
+ } catch (err) {
10689
+ const raw = err instanceof Error ? err.message : String(err);
10690
+ const msg = raw.includes("Cannot find module") || raw.includes("Module not found") || raw.includes("Failed to fetch") ? "Install @stripe/crypto to use Stripe Link: npm install @stripe/crypto" : `Failed to initialize Stripe SDK: ${raw}`;
10691
+ setError(msg);
10692
+ console.error("[PayWithStripeLink]", msg);
10693
+ return null;
10694
+ } finally {
10695
+ setIsLoading(false);
10696
+ initPromiseRef.current = null;
10109
10697
  }
10110
- console.log("[PayWithStripeLink] Stripe SDK initialized successfully");
10111
- coordinatorRef.current = instance;
10112
- setCoordinator(instance);
10113
- return coordinatorRef.current;
10114
- } catch (err) {
10115
- const raw = err instanceof Error ? err.message : String(err);
10116
- const msg = raw.includes("Cannot find module") || raw.includes("Module not found") || raw.includes("Failed to fetch") ? "Install @stripe/crypto to use Stripe Link: npm install @stripe/crypto" : `Failed to initialize Stripe SDK: ${raw}`;
10117
- setError(msg);
10118
- console.error("[PayWithStripeLink]", msg);
10119
- return null;
10120
- } finally {
10121
- setIsLoading(false);
10122
- }
10698
+ })();
10699
+ initPromiseRef.current = promise;
10700
+ return promise;
10123
10701
  }, [stripePublishableKey]);
10702
+ useEffect23(() => {
10703
+ if (!coordinatorRef.current) return;
10704
+ if (coordinatorThemeRef.current === isDark) return;
10705
+ coordinatorRef.current.destroy();
10706
+ coordinatorRef.current = null;
10707
+ coordinatorThemeRef.current = null;
10708
+ setCoordinator(null);
10709
+ }, [isDark, coordinator]);
10124
10710
  useEffect23(() => {
10125
10711
  return () => {
10126
10712
  coordinatorRef.current?.destroy();
10127
10713
  coordinatorRef.current = null;
10714
+ coordinatorThemeRef.current = null;
10128
10715
  };
10129
10716
  }, []);
10130
10717
  return { coordinator, isLoading, error, initialize };
@@ -10280,7 +10867,7 @@ function PayWithStripeLink({
10280
10867
  onDepositSuccess,
10281
10868
  onDepositError
10282
10869
  }) {
10283
- const { colors: colors2, fonts, components } = useTheme();
10870
+ const { colors: colors2, fonts, components, isDark } = useTheme();
10284
10871
  const [step, setStepInternal] = useState29(controlledStep ?? "amount");
10285
10872
  const setStep = useCallback4(
10286
10873
  (s) => {
@@ -10289,6 +10876,8 @@ function PayWithStripeLink({
10289
10876
  },
10290
10877
  [onStepChange]
10291
10878
  );
10879
+ const stepRef = useRef8(step);
10880
+ stepRef.current = step;
10292
10881
  useEffect24(() => {
10293
10882
  if (controlledStep && controlledStep !== step) {
10294
10883
  setStepInternal(controlledStep);
@@ -10356,7 +10945,7 @@ function PayWithStripeLink({
10356
10945
  isLoading: sdkLoading,
10357
10946
  error: sdkError,
10358
10947
  initialize
10359
- } = useStripeOnramp(resolvedStripeKey);
10948
+ } = useStripeOnramp(resolvedStripeKey, isDark);
10360
10949
  useEffect24(() => {
10361
10950
  if (step === "auth") {
10362
10951
  console.log("[PayWithStripeLink] Auth step state:", {
@@ -10379,6 +10968,9 @@ function PayWithStripeLink({
10379
10968
  const [errorReturnStep, setErrorReturnStep] = useState29("email");
10380
10969
  const [authIntentId, setAuthIntentId] = useState29(null);
10381
10970
  const sdkAuthenticatedRef = useRef8(false);
10971
+ const everAuthenticatedRef = useRef8(false);
10972
+ const reauthReturnStepRef = useRef8(null);
10973
+ const attemptedWalletReauthRef = useRef8(false);
10382
10974
  const pendingAddPaymentRef = useRef8(false);
10383
10975
  const autoOpenedAddPaymentRef = useRef8(false);
10384
10976
  const checkoutGenRef = useRef8(0);
@@ -10577,6 +11169,7 @@ function PayWithStripeLink({
10577
11169
  if (step === "auth" && (pendingAddPaymentRef.current || !!accessTokenRef.current)) {
10578
11170
  pendingAddPaymentRef.current = false;
10579
11171
  autoOpenedAddPaymentRef.current = false;
11172
+ reauthReturnStepRef.current = null;
10580
11173
  setError(null);
10581
11174
  setStep("amount");
10582
11175
  return true;
@@ -10594,17 +11187,34 @@ function PayWithStripeLink({
10594
11187
  );
10595
11188
  const [selectedPaymentToken, setSelectedPaymentToken] = useState29(null);
10596
11189
  const [paymentDisplay, setPaymentDisplay] = useState29(null);
11190
+ const selectedTokenSingleUseRef = useRef8(false);
10597
11191
  const selectPaymentToken = useCallback4(
10598
11192
  (token) => {
11193
+ selectedTokenSingleUseRef.current = !!token.singleUse;
10599
11194
  setSelectedPaymentToken(token.id);
10600
11195
  setPaymentDisplay(
10601
11196
  token.label ? { label: token.label, sublabel: token.sublabel, brand: token.brand } : null
10602
11197
  );
10603
11198
  setStoredSelectedTokenState(token);
10604
- if (customerId) setStoredSelectedToken(customerId, token);
11199
+ if (customerId && !token.singleUse) setStoredSelectedToken(customerId, token);
10605
11200
  },
10606
11201
  [customerId]
10607
11202
  );
11203
+ const clearSelectedPaymentToken = useCallback4(() => {
11204
+ selectedTokenSingleUseRef.current = false;
11205
+ const persisted = customerId ? getStoredSelectedToken(customerId) : null;
11206
+ if (persisted && !persisted.singleUse) {
11207
+ setSelectedPaymentToken(persisted.id);
11208
+ setPaymentDisplay(
11209
+ persisted.label ? { label: persisted.label, sublabel: persisted.sublabel, brand: persisted.brand } : null
11210
+ );
11211
+ setStoredSelectedTokenState(persisted);
11212
+ } else {
11213
+ setSelectedPaymentToken(null);
11214
+ setPaymentDisplay(null);
11215
+ setStoredSelectedTokenState(null);
11216
+ }
11217
+ }, [customerId]);
10608
11218
  const displayPaymentTokens = (() => {
10609
11219
  const apiDisplay = paymentTokens.map((t13) => ({ id: t13.id, ...formatPaymentToken(t13) }));
10610
11220
  if (storedSelectedToken && !paymentTokens.some((t13) => t13.id === storedSelectedToken.id)) {
@@ -10843,6 +11453,10 @@ function PayWithStripeLink({
10843
11453
  if (!resolvedStripeKey) return;
10844
11454
  initialize();
10845
11455
  }, [coordinator, sdkLoading, sdkError, resolvedStripeKey, initialize]);
11456
+ useEffect24(() => {
11457
+ sdkAuthenticatedRef.current = false;
11458
+ attemptedWalletReauthRef.current = false;
11459
+ }, [coordinator]);
10846
11460
  const handleAuthInterrupted = useCallback4(
10847
11461
  (message) => {
10848
11462
  const alreadySignedIn = !!accessTokenRef.current;
@@ -10856,6 +11470,26 @@ function PayWithStripeLink({
10856
11470
  },
10857
11471
  [emailProp, setStep]
10858
11472
  );
11473
+ const reauthenticateSdk = useCallback4(async () => {
11474
+ const current = stepRef.current;
11475
+ const preAuthSteps = ["email", "register", "auth"];
11476
+ reauthReturnStepRef.current = preAuthSteps.includes(current) ? null : current;
11477
+ const knownEmail = (email || emailProp || "").trim();
11478
+ if (!knownEmail) {
11479
+ reauthReturnStepRef.current = null;
11480
+ setStep("email");
11481
+ return;
11482
+ }
11483
+ try {
11484
+ const authResult = await stripeCreateAuthIntent(knownEmail, publishableKey);
11485
+ setAuthIntentId(authResult.auth_intent_id);
11486
+ setStep("auth");
11487
+ } catch (err) {
11488
+ reauthReturnStepRef.current = null;
11489
+ setError(err instanceof Error ? err.message : "Failed to re-verify. Please try again.");
11490
+ setStep("email");
11491
+ }
11492
+ }, [email, emailProp, publishableKey]);
10859
11493
  useEffect24(() => {
10860
11494
  if (step !== "auth" || !coordinator || !authIntentId) return;
10861
11495
  let mounted = true;
@@ -10866,6 +11500,7 @@ function PayWithStripeLink({
10866
11500
  if (result.result === "success" && result.crypto_customer_id) {
10867
11501
  setCustomerId(result.crypto_customer_id);
10868
11502
  sdkAuthenticatedRef.current = true;
11503
+ everAuthenticatedRef.current = true;
10869
11504
  const tokens = await stripeExchangeTokens(authIntentId, publishableKey);
10870
11505
  setOauthToken(tokens.access_token);
10871
11506
  const refresh = extractRefreshToken(tokens);
@@ -10879,16 +11514,22 @@ function PayWithStripeLink({
10879
11514
  tokens.expires_in ?? 3600,
10880
11515
  email.trim()
10881
11516
  );
11517
+ const resumeStep = reauthReturnStepRef.current;
11518
+ reauthReturnStepRef.current = null;
10882
11519
  if (pendingAddPaymentRef.current) {
10883
11520
  setStep("payment");
11521
+ } else if (resumeStep) {
11522
+ setStep(resumeStep);
10884
11523
  } else {
10885
11524
  setStep("kyc");
10886
11525
  }
10887
11526
  } else if (result.result === "abandoned") {
10888
11527
  pendingAddPaymentRef.current = false;
11528
+ reauthReturnStepRef.current = null;
10889
11529
  handleAuthInterrupted("Verification was cancelled.");
10890
11530
  } else if (result.result === "declined") {
10891
11531
  pendingAddPaymentRef.current = false;
11532
+ reauthReturnStepRef.current = null;
10892
11533
  handleAuthInterrupted("You must consent to continue.");
10893
11534
  }
10894
11535
  });
@@ -10900,6 +11541,7 @@ function PayWithStripeLink({
10900
11541
  }
10901
11542
  } catch (err) {
10902
11543
  if (!mounted) return;
11544
+ reauthReturnStepRef.current = null;
10903
11545
  const msg = err instanceof Error ? err.message : "Verification was cancelled.";
10904
11546
  handleAuthInterrupted(msg);
10905
11547
  }
@@ -11001,6 +11643,10 @@ function PayWithStripeLink({
11001
11643
  };
11002
11644
  const handleKycSubmit = async () => {
11003
11645
  if (!coordinator) return;
11646
+ if (!sdkAuthenticatedRef.current) {
11647
+ await reauthenticateSdk();
11648
+ return;
11649
+ }
11004
11650
  let kycInfo;
11005
11651
  if (kycFormMode === "address") {
11006
11652
  if (!kycForm.givenName.trim() || !kycForm.surname.trim()) {
@@ -11060,6 +11706,10 @@ function PayWithStripeLink({
11060
11706
  let mounted = true;
11061
11707
  const verify = async () => {
11062
11708
  try {
11709
+ if (!sdkAuthenticatedRef.current) {
11710
+ await reauthenticateSdk();
11711
+ return;
11712
+ }
11063
11713
  const result = await coordinator.verifyDocuments();
11064
11714
  if (!mounted) return;
11065
11715
  const outcome = typeof result === "string" ? result : result?.result;
@@ -11079,7 +11729,7 @@ function PayWithStripeLink({
11079
11729
  return () => {
11080
11730
  mounted = false;
11081
11731
  };
11082
- }, [step, coordinator]);
11732
+ }, [step, coordinator, reauthenticateSdk]);
11083
11733
  useEffect24(() => {
11084
11734
  if (step !== "wallet" || !oauthToken || !customerId) return;
11085
11735
  let mounted = true;
@@ -11105,6 +11755,10 @@ function PayWithStripeLink({
11105
11755
  if (!coordinator) {
11106
11756
  return;
11107
11757
  }
11758
+ if (!sdkAuthenticatedRef.current) {
11759
+ await reauthenticateSdk();
11760
+ return;
11761
+ }
11108
11762
  await coordinator.registerWalletAddress(onrampWalletAddress, network);
11109
11763
  }
11110
11764
  if (mounted) setRegisteredWalletKey(walletKeyFor(onrampWalletAddress));
@@ -11121,7 +11775,15 @@ function PayWithStripeLink({
11121
11775
  return () => {
11122
11776
  mounted = false;
11123
11777
  };
11124
- }, [step, oauthToken, customerId, publishableKey, onrampWalletAddress, coordinator]);
11778
+ }, [
11779
+ step,
11780
+ oauthToken,
11781
+ customerId,
11782
+ publishableKey,
11783
+ onrampWalletAddress,
11784
+ coordinator,
11785
+ reauthenticateSdk
11786
+ ]);
11125
11787
  useEffect24(() => {
11126
11788
  if (step !== "amount" && step !== "review") return;
11127
11789
  if (!customerId || !oauthToken || !onrampWalletAddress) return;
@@ -11150,6 +11812,10 @@ function PayWithStripeLink({
11150
11812
  if (coordinator && sdkAuthenticatedRef.current) {
11151
11813
  await coordinator.registerWalletAddress(onrampWalletAddress, network);
11152
11814
  if (!cancelled) setRegisteredWalletKey(walletKeyFor(onrampWalletAddress));
11815
+ } else if (coordinator && everAuthenticatedRef.current && !attemptedWalletReauthRef.current) {
11816
+ attemptedWalletReauthRef.current = true;
11817
+ await reauthenticateSdk();
11818
+ return;
11153
11819
  }
11154
11820
  } catch {
11155
11821
  }
@@ -11167,7 +11833,8 @@ function PayWithStripeLink({
11167
11833
  coordinator,
11168
11834
  stripeDestNetwork,
11169
11835
  stripeDestChainType,
11170
- publishableKey
11836
+ publishableKey,
11837
+ reauthenticateSdk
11171
11838
  ]);
11172
11839
  useEffect24(() => {
11173
11840
  if (step !== "payment" || !oauthToken || !customerId) return;
@@ -11264,6 +11931,32 @@ function PayWithStripeLink({
11264
11931
  if (!mounted) return;
11265
11932
  const tokenId = result.cryptoPaymentToken;
11266
11933
  let display = null;
11934
+ const pmd = result.paymentMethodDetails;
11935
+ if (pmd) {
11936
+ display = formatPaymentToken(pmd);
11937
+ }
11938
+ const singleUse = pmd?.type === "card" && !!pmd.card.wallet;
11939
+ const applyAndAdvance = () => {
11940
+ selectPaymentToken({
11941
+ id: tokenId,
11942
+ label: display?.label ?? "",
11943
+ sublabel: display?.sublabel ?? "",
11944
+ brand: display?.brand,
11945
+ singleUse
11946
+ });
11947
+ autoOpenedAddPaymentRef.current = false;
11948
+ setStep("amount");
11949
+ };
11950
+ if (display) {
11951
+ applyAndAdvance();
11952
+ if (customerId && accessTokenRef.current) {
11953
+ stripeListPaymentTokens(customerId, accessTokenRef.current, publishableKey).then((tokens) => {
11954
+ if (mounted) setPaymentTokens(tokens.data);
11955
+ }).catch(() => {
11956
+ });
11957
+ }
11958
+ return;
11959
+ }
11267
11960
  try {
11268
11961
  if (customerId && accessTokenRef.current) {
11269
11962
  const tokens = await stripeListPaymentTokens(
@@ -11271,28 +11964,15 @@ function PayWithStripeLink({
11271
11964
  accessTokenRef.current,
11272
11965
  publishableKey
11273
11966
  );
11967
+ if (!mounted) return;
11968
+ setPaymentTokens(tokens.data);
11274
11969
  const match = tokens.data.find((t13) => t13.id === tokenId);
11275
- if (match) {
11276
- display = formatPaymentToken(match);
11277
- setPaymentTokens(tokens.data);
11278
- }
11970
+ if (match) display = formatPaymentToken(match);
11279
11971
  }
11280
11972
  } catch {
11281
11973
  }
11282
- if (!display) {
11283
- const dd = result.displayData;
11284
- if (dd?.label) {
11285
- display = { label: dd.label, sublabel: dd.sublabel ?? "", brand: dd.type };
11286
- }
11287
- }
11288
- selectPaymentToken({
11289
- id: tokenId,
11290
- label: display?.label ?? "",
11291
- sublabel: display?.sublabel ?? "",
11292
- brand: display?.brand
11293
- });
11294
- autoOpenedAddPaymentRef.current = false;
11295
- setStep("amount");
11974
+ if (!mounted) return;
11975
+ applyAndAdvance();
11296
11976
  }
11297
11977
  );
11298
11978
  if (mounted && el) {
@@ -11329,6 +12009,10 @@ function PayWithStripeLink({
11329
12009
  const handleCheckout = async () => {
11330
12010
  if (!amount || !oauthToken || !customerId || !selectedPaymentToken || !onrampWalletAddress || !isOnrampWalletRegistered || !coordinator)
11331
12011
  return;
12012
+ if (!sdkAuthenticatedRef.current) {
12013
+ await reauthenticateSdk();
12014
+ return;
12015
+ }
11332
12016
  const gen = ++checkoutGenRef.current;
11333
12017
  const isStale = () => checkoutGenRef.current !== gen;
11334
12018
  const surfaceStepUpOnAmount = (err, message, tier) => {
@@ -11440,6 +12124,7 @@ function PayWithStripeLink({
11440
12124
  executionReconciledSessionRef.current = null;
11441
12125
  setConfirmedSessionId(activeSessionId);
11442
12126
  setSessionStatus(STRIPE_SESSION_STATUS.FULFILLMENT_PROCESSING);
12127
+ if (selectedTokenSingleUseRef.current) clearSelectedPaymentToken();
11443
12128
  setStep("success");
11444
12129
  } else if (lastError === "quote_rate_drifted" || lastError === "charged_with_expired_quote") {
11445
12130
  setSessionForCheckout(null);
@@ -11491,6 +12176,25 @@ function PayWithStripeLink({
11491
12176
  sessionForCheckoutRef.current = s;
11492
12177
  setSessionForCheckoutState(s);
11493
12178
  };
12179
+ const loadAndSelectPreferredToken = useCallback4(async () => {
12180
+ if (!customerId) return;
12181
+ try {
12182
+ const existing = await withTokenRefresh(
12183
+ (tok) => stripeListPaymentTokens(customerId, tok, publishableKey)
12184
+ );
12185
+ setPaymentTokens(existing.data);
12186
+ const stored = getStoredSelectedToken(customerId);
12187
+ const merged = [
12188
+ ...existing.data.map((t13) => ({ id: t13.id, ...formatPaymentToken(t13) })),
12189
+ ...stored && !existing.data.some((t13) => t13.id === stored.id) ? [stored] : []
12190
+ ];
12191
+ if (merged.length > 0) {
12192
+ const preferred = stored && merged.find((m) => m.id === stored.id) || merged[0];
12193
+ selectPaymentToken(preferred);
12194
+ }
12195
+ } catch {
12196
+ }
12197
+ }, [customerId, publishableKey, selectPaymentToken, withTokenRefresh]);
11494
12198
  const handleDone = () => {
11495
12199
  handledTerminalSessionRef.current = null;
11496
12200
  executionReconciledSessionRef.current = null;
@@ -11498,6 +12202,7 @@ function PayWithStripeLink({
11498
12202
  setSessionStatus(null);
11499
12203
  setSessionForCheckout(null);
11500
12204
  setStep("amount");
12205
+ if (!selectedPaymentToken) void loadAndSelectPreferredToken();
11501
12206
  };
11502
12207
  const [quoteError, setQuoteError] = useState29(null);
11503
12208
  const [quoteNonce, setQuoteNonce] = useState29(0);
@@ -12279,10 +12984,16 @@ function PayWithStripeLink({
12279
12984
  return /* @__PURE__ */ jsxs41(
12280
12985
  "div",
12281
12986
  {
12282
- className: "uf-flex uf-flex-col uf-py-4 uf-overflow-y-auto uf-max-h-[70vh]",
12987
+ className: "uf-flex uf-flex-col uf-py-4 uf-overflow-y-auto uf-overflow-x-hidden uf-max-h-[70vh]",
12283
12988
  style: { backgroundColor: colors2.background },
12284
12989
  children: [
12285
- /* @__PURE__ */ jsx44("div", { ref: paymentMountRef, className: "uf-w-full uf-flex-1 uf-overflow-y-auto" }),
12990
+ /* @__PURE__ */ jsx44(
12991
+ "div",
12992
+ {
12993
+ ref: paymentMountRef,
12994
+ className: "uf-w-full uf-flex-1 uf-overflow-y-auto uf-overflow-x-hidden"
12995
+ }
12996
+ ),
12286
12997
  !stripePaymentUIReady && /* @__PURE__ */ jsx44("div", { className: "uf-flex uf-items-center uf-justify-center uf-py-8", children: /* @__PURE__ */ jsx44(Loader25, { className: "uf-w-8 uf-h-8 uf-animate-spin", style: { color: colors2.primary } }) }),
12287
12998
  error && /* @__PURE__ */ jsx44(
12288
12999
  "div",
@@ -12300,7 +13011,7 @@ function PayWithStripeLink({
12300
13011
  return /* @__PURE__ */ jsxs41(
12301
13012
  "div",
12302
13013
  {
12303
- className: "uf-flex uf-flex-col uf-py-4 uf-overflow-y-auto uf-max-h-[70vh]",
13014
+ className: "uf-flex uf-flex-col uf-py-4 uf-overflow-y-auto uf-overflow-x-hidden uf-max-h-[70vh]",
12304
13015
  style: { backgroundColor: colors2.background },
12305
13016
  children: [
12306
13017
  displayPaymentTokens.length === 0 && !loading && /* @__PURE__ */ jsx44("div", { className: "uf-px-1 uf-mb-3 uf-text-center", children: /* @__PURE__ */ jsx44(
@@ -12337,7 +13048,8 @@ function PayWithStripeLink({
12337
13048
  {
12338
13049
  brand: display.brand,
12339
13050
  size: 28,
12340
- fallbackColor: colors2.foregroundMuted
13051
+ fallbackColor: colors2.foregroundMuted,
13052
+ monochromeColor: monochromeOn(components.card.backgroundColor)
12341
13053
  }
12342
13054
  )
12343
13055
  }
@@ -12922,7 +13634,8 @@ function PayWithStripeLink({
12922
13634
  {
12923
13635
  brand: paymentDisplay.brand,
12924
13636
  size: 20,
12925
- fallbackColor: colors2.foregroundMuted
13637
+ fallbackColor: colors2.foregroundMuted,
13638
+ monochromeColor: monochromeOn(components.card.backgroundColor)
12926
13639
  }
12927
13640
  ),
12928
13641
  /* @__PURE__ */ jsx44(
@@ -13408,7 +14121,7 @@ function PayWithStripeLink({
13408
14121
  }
13409
14122
 
13410
14123
  // src/components/deposits/CoinbaseConnect.tsx
13411
- import { useState as useState30, useEffect as useEffect25, useCallback as useCallback5, useMemo as useMemo6, useRef as useRef9 } from "react";
14124
+ import { useState as useState30, useEffect as useEffect25, useCallback as useCallback5, useMemo as useMemo7, useRef as useRef9 } from "react";
13412
14125
  import {
13413
14126
  ChevronRight as ChevronRight15,
13414
14127
  ChevronDown as ChevronDown4,
@@ -13417,7 +14130,7 @@ import {
13417
14130
  Hand,
13418
14131
  Loader2 as Loader26,
13419
14132
  CheckCircle2 as CheckCircle23,
13420
- AlertCircle as AlertCircle2
14133
+ AlertCircle
13421
14134
  } from "lucide-react";
13422
14135
  import {
13423
14136
  getIntegrationExchanges,
@@ -13430,8 +14143,39 @@ import {
13430
14143
  IntegrationProvider
13431
14144
  } from "@unifold/core";
13432
14145
 
14146
+ // src/hooks/use-project-config.ts
14147
+ import { useQuery as useQuery8, keepPreviousData } from "@tanstack/react-query";
14148
+ import { getProjectConfig } from "@unifold/core";
14149
+ function useProjectConfig({
14150
+ publishableKey,
14151
+ enabled = true,
14152
+ countryCode,
14153
+ subdivisionCode
14154
+ }) {
14155
+ const {
14156
+ data: projectConfig,
14157
+ isLoading,
14158
+ error
14159
+ } = useQuery8({
14160
+ // Country is part of the key so a region change refetches the region-aware
14161
+ // config. Omitted when undefined so callers that don't pass a country keep
14162
+ // sharing the base cache entry.
14163
+ queryKey: countryCode ? ["unifold", "projectConfig", publishableKey, countryCode, subdivisionCode ?? null] : ["unifold", "projectConfig", publishableKey],
14164
+ queryFn: () => getProjectConfig(publishableKey, countryCode ? { countryCode, subdivisionCode } : void 0),
14165
+ enabled,
14166
+ // Keep the previous (e.g. no-country) config visible while the region-aware
14167
+ // config refetches after the country resolves, so unrelated config-driven
14168
+ // UI doesn't flash back to defaults.
14169
+ placeholderData: keepPreviousData,
14170
+ staleTime: 1e3 * 60 * 30,
14171
+ refetchOnMount: true,
14172
+ refetchOnWindowFocus: true
14173
+ });
14174
+ return { projectConfig, isLoading, error: error ?? null };
14175
+ }
14176
+
13433
14177
  // src/hooks/use-supported-deposit-tokens.ts
13434
- import { useQuery as useQuery8 } from "@tanstack/react-query";
14178
+ import { useQuery as useQuery9 } from "@tanstack/react-query";
13435
14179
  import {
13436
14180
  getSupportedDepositTokens
13437
14181
  } from "@unifold/core";
@@ -13446,7 +14190,7 @@ function useSupportedDepositTokens(publishableKey, options) {
13446
14190
  ...options?.product_type ? { product_type: options.product_type } : {}
13447
14191
  };
13448
14192
  const hasFilteredOptions = Object.keys(filteredOptions).length > 0;
13449
- return useQuery8({
14193
+ return useQuery9({
13450
14194
  queryKey: [
13451
14195
  "unifold",
13452
14196
  "supportedDepositTokens",
@@ -13467,7 +14211,7 @@ function useSupportedDepositTokens(publishableKey, options) {
13467
14211
  }
13468
14212
 
13469
14213
  // src/hooks/use-integration-transfer-default-token.ts
13470
- import { useQuery as useQuery9 } from "@tanstack/react-query";
14214
+ import { useQuery as useQuery10 } from "@tanstack/react-query";
13471
14215
  import {
13472
14216
  getIntegrationTransferDefaultToken
13473
14217
  } from "@unifold/core";
@@ -13476,7 +14220,7 @@ function useIntegrationTransferDefaultToken({
13476
14220
  publishableKey,
13477
14221
  enabled = true
13478
14222
  }) {
13479
- return useQuery9({
14223
+ return useQuery10({
13480
14224
  queryKey: [
13481
14225
  "unifold",
13482
14226
  "integrationTransferDefaultToken",
@@ -13557,12 +14301,12 @@ function CoinbaseConnect({
13557
14301
  destination_chain_id: destinationChainId,
13558
14302
  destination_chain_type: destinationChainType
13559
14303
  });
13560
- const supportedSymbols = useMemo6(() => {
14304
+ const supportedSymbols = useMemo7(() => {
13561
14305
  const set = /* @__PURE__ */ new Set();
13562
14306
  supportedTokensData?.data.forEach((token) => set.add(token.symbol.toLowerCase()));
13563
14307
  return set;
13564
14308
  }, [supportedTokensData]);
13565
- const stablecoinSymbols = useMemo6(() => {
14309
+ const stablecoinSymbols = useMemo7(() => {
13566
14310
  const set = /* @__PURE__ */ new Set();
13567
14311
  supportedTokensData?.data.forEach((token) => {
13568
14312
  if (token.is_stablecoin) set.add(token.symbol.toLowerCase());
@@ -13595,12 +14339,12 @@ function CoinbaseConnect({
13595
14339
  const [transferDepositWalletId, setTransferDepositWalletId] = useState30(
13596
14340
  void 0
13597
14341
  );
13598
- const exchangeSupportedCurrencies = useMemo6(() => {
14342
+ const exchangeSupportedCurrencies = useMemo7(() => {
13599
14343
  const set = /* @__PURE__ */ new Set();
13600
14344
  selectedExchange?.supported_currencies.forEach((c) => set.add(c.toLowerCase()));
13601
14345
  return set;
13602
14346
  }, [selectedExchange]);
13603
- const defaultTokenParams = useMemo6(
14347
+ const defaultTokenParams = useMemo7(
13604
14348
  () => selectedAsset ? {
13605
14349
  integration_provider: IntegrationProvider.COINBASE,
13606
14350
  source_currency: selectedAsset.currency.toLowerCase(),
@@ -13623,7 +14367,7 @@ function CoinbaseConnect({
13623
14367
  params: defaultTokenParams,
13624
14368
  publishableKey
13625
14369
  });
13626
- const defaultSourceCurrency = useMemo6(
14370
+ const defaultSourceCurrency = useMemo7(
13627
14371
  () => resolveDefaultSourceSymbol(supportedTokensData?.data, {
13628
14372
  defaultSourceChainType,
13629
14373
  defaultSourceChainId,
@@ -13638,7 +14382,7 @@ function CoinbaseConnect({
13638
14382
  defaultSourceSymbol
13639
14383
  ]
13640
14384
  );
13641
- const sortedHoldings = useMemo6(() => {
14385
+ const sortedHoldings = useMemo7(() => {
13642
14386
  const supported = [];
13643
14387
  const unsupported = [];
13644
14388
  holdings.forEach((account) => {
@@ -13658,7 +14402,7 @@ function CoinbaseConnect({
13658
14402
  }
13659
14403
  return [...supported, ...unsupported];
13660
14404
  }, [holdings, supportedSymbols, exchangeSupportedCurrencies, defaultSourceCurrency]);
13661
- const selectedHoldingIsSupported = useMemo6(() => {
14405
+ const selectedHoldingIsSupported = useMemo7(() => {
13662
14406
  if (!selectedHolding) return false;
13663
14407
  const currencyLower = selectedHolding.currency.toLowerCase();
13664
14408
  return (supportedSymbols.size === 0 || supportedSymbols.has(currencyLower)) && (exchangeSupportedCurrencies.size === 0 || exchangeSupportedCurrencies.has(currencyLower));
@@ -13805,7 +14549,7 @@ function CoinbaseConnect({
13805
14549
  if (pollRef.current) clearInterval(pollRef.current);
13806
14550
  };
13807
14551
  }, []);
13808
- const minDepositUsd = useMemo6(() => {
14552
+ const minDepositUsd = useMemo7(() => {
13809
14553
  if (!selectedAsset || !defaultTokenData) return 0;
13810
14554
  const supportedToken = supportedTokensData?.data.find(
13811
14555
  (t14) => t14.symbol.toLowerCase() === selectedAsset.currency.toLowerCase()
@@ -13816,7 +14560,7 @@ function CoinbaseConnect({
13816
14560
  );
13817
14561
  return matchingChain?.minimum_deposit_amount_usd ?? Math.min(...supportedToken.chains.map((c) => c.minimum_deposit_amount_usd));
13818
14562
  }, [selectedAsset, supportedTokensData, defaultTokenData]);
13819
- const estimatedProcessingTime = useMemo6(() => {
14563
+ const estimatedProcessingTime = useMemo7(() => {
13820
14564
  if (!selectedAsset || !defaultTokenData) return null;
13821
14565
  const supportedToken = supportedTokensData?.data.find(
13822
14566
  (t14) => t14.symbol.toLowerCase() === selectedAsset.currency.toLowerCase()
@@ -15409,7 +16153,7 @@ function CoinbaseConnect({
15409
16153
  {
15410
16154
  className: "uf-w-16 uf-h-16 uf-rounded-full uf-flex uf-items-center uf-justify-center uf-mb-6",
15411
16155
  style: { backgroundColor: `${colors2.error}20` },
15412
- children: /* @__PURE__ */ jsx45(AlertCircle2, { className: "uf-w-8 uf-h-8", style: { color: colors2.error } })
16156
+ children: /* @__PURE__ */ jsx45(AlertCircle, { className: "uf-w-8 uf-h-8", style: { color: colors2.error } })
15413
16157
  }
15414
16158
  ),
15415
16159
  /* @__PURE__ */ jsx45(
@@ -15456,13 +16200,13 @@ function CoinbaseConnect({
15456
16200
  CoinbaseConnect.displayName = "CoinbaseConnect";
15457
16201
 
15458
16202
  // src/hooks/use-exchanges.ts
15459
- import { useQuery as useQuery10 } from "@tanstack/react-query";
16203
+ import { useQuery as useQuery11 } from "@tanstack/react-query";
15460
16204
  import { getExchanges } from "@unifold/core";
15461
16205
  function useExchanges({
15462
16206
  publishableKey,
15463
16207
  enabled = true
15464
16208
  }) {
15465
- const { data: exchanges = [], isLoading } = useQuery10({
16209
+ const { data: exchanges = [], isLoading } = useQuery11({
15466
16210
  queryKey: ["unifold", "exchanges", publishableKey],
15467
16211
  queryFn: () => getExchanges(void 0, publishableKey).then((res) => res.data),
15468
16212
  enabled,
@@ -15474,13 +16218,13 @@ function useExchanges({
15474
16218
  }
15475
16219
 
15476
16220
  // src/hooks/use-apple-pay-providers.ts
15477
- import { useQuery as useQuery11 } from "@tanstack/react-query";
16221
+ import { useQuery as useQuery12 } from "@tanstack/react-query";
15478
16222
  import { getApplePayProviders } from "@unifold/core";
15479
16223
  function useApplePayProviders({
15480
16224
  publishableKey,
15481
16225
  enabled = true
15482
16226
  }) {
15483
- const { data: providers, isLoading } = useQuery11({
16227
+ const { data: providers, isLoading } = useQuery12({
15484
16228
  queryKey: ["unifold", "applePayProviders", publishableKey],
15485
16229
  queryFn: () => getApplePayProviders(publishableKey),
15486
16230
  enabled,
@@ -15503,50 +16247,29 @@ import {
15503
16247
  } from "@unifold/core";
15504
16248
 
15505
16249
  // src/hooks/use-allowed-country.ts
15506
- import { useQuery as useQuery12 } from "@tanstack/react-query";
15507
- import {
15508
- getIpAddress as getIpAddress2,
15509
- getProjectConfig as getProjectConfig2
15510
- } from "@unifold/core";
15511
16250
  function useAllowedCountry(publishableKey) {
16251
+ const { userIpInfo, isLoading: isIpLoading, error: ipError } = useUserIp();
15512
16252
  const {
15513
- data: ipData,
15514
- isLoading: isIpLoading,
15515
- error: ipError
15516
- } = useQuery12({
15517
- queryKey: ["unifold", "ipAddress"],
15518
- queryFn: () => getIpAddress2(),
15519
- refetchOnMount: false,
15520
- refetchOnReconnect: true,
15521
- refetchOnWindowFocus: false,
15522
- staleTime: 1e3 * 60 * 60,
15523
- // 1 hour
15524
- gcTime: 1e3 * 60 * 60 * 24
15525
- // 24 hours
15526
- });
15527
- const {
15528
- data: configData,
16253
+ projectConfig,
15529
16254
  isLoading: isConfigLoading,
15530
16255
  error: configError
15531
- } = useQuery12({
15532
- queryKey: ["unifold", "projectConfig", publishableKey],
15533
- queryFn: () => getProjectConfig2(publishableKey),
15534
- refetchOnMount: false,
15535
- refetchOnReconnect: true,
15536
- refetchOnWindowFocus: false,
15537
- staleTime: 1e3 * 60 * 5,
15538
- // 5 minutes
15539
- gcTime: 1e3 * 60 * 60
15540
- // 1 hour
16256
+ } = useProjectConfig({
16257
+ publishableKey,
16258
+ // Wait for the IP so we issue a single country-aware config request rather
16259
+ // than a country-less fetch followed by a country-aware refetch. Shares the
16260
+ // query key with DepositModal's useProjectConfig, so they dedupe.
16261
+ enabled: !isIpLoading,
16262
+ countryCode: userIpInfo?.alpha2,
16263
+ subdivisionCode: userIpInfo?.subdivisionCode ?? void 0
15541
16264
  });
15542
16265
  const isLoading = isIpLoading || isConfigLoading;
15543
16266
  const error = ipError || configError || null;
16267
+ const userSubdivision = userIpInfo?.subdivisionCode || userIpInfo?.state || "";
15544
16268
  let isAllowed = null;
15545
- if (ipData && configData) {
15546
- const blockedCodes = configData.blocked_country_codes || [];
15547
- const blockedSubdivisions = configData.blocked_country_subdivisions || [];
15548
- const userCountryUpper = ipData.alpha2.toUpperCase();
15549
- const userSubdivision = ipData.subdivision_code || ipData.state || "";
16269
+ if (userIpInfo && projectConfig) {
16270
+ const blockedCodes = projectConfig.blocked_country_codes || [];
16271
+ const blockedSubdivisions = projectConfig.blocked_country_subdivisions || [];
16272
+ const userCountryUpper = userIpInfo.alpha2.toUpperCase();
15550
16273
  const userSubdivisionUpper = userSubdivision.toUpperCase();
15551
16274
  const isCountryBlocked = blockedCodes.some((code) => code.toUpperCase() === userCountryUpper);
15552
16275
  const isSubdivisionBlocked = blockedSubdivisions.some((entry) => {
@@ -15555,12 +16278,11 @@ function useAllowedCountry(publishableKey) {
15555
16278
  });
15556
16279
  isAllowed = !isCountryBlocked && !isSubdivisionBlocked;
15557
16280
  }
15558
- const subdivisionCode = ipData?.subdivision_code || ipData?.state || "" || null;
15559
16281
  return {
15560
16282
  isAllowed,
15561
- alpha2: ipData?.alpha2 ?? null,
15562
- country: ipData?.country ?? null,
15563
- subdivisionCode,
16283
+ alpha2: userIpInfo?.alpha2 ?? null,
16284
+ country: userIpInfo?.country ?? null,
16285
+ subdivisionCode: userSubdivision || null,
15564
16286
  isLoading,
15565
16287
  error
15566
16288
  };
@@ -15627,7 +16349,7 @@ function useAddressValidation({
15627
16349
  }
15628
16350
 
15629
16351
  // src/components/deposits/TransferCryptoSingleInput.tsx
15630
- import { useState as useState36, useEffect as useEffect30, useMemo as useMemo9 } from "react";
16352
+ import { useState as useState36, useEffect as useEffect30, useMemo as useMemo10 } from "react";
15631
16353
  import {
15632
16354
  ChevronDown as ChevronDown5,
15633
16355
  ChevronUp as ChevronUp3,
@@ -15986,7 +16708,7 @@ function DepositsModal({
15986
16708
  }
15987
16709
 
15988
16710
  // src/components/deposits/TokenSelectorSheet.tsx
15989
- import { useState as useState32, useMemo as useMemo8, useEffect as useEffect28 } from "react";
16711
+ import { useState as useState32, useMemo as useMemo9, useEffect as useEffect28 } from "react";
15990
16712
  import { ArrowLeft as ArrowLeft2, X as X4 } from "lucide-react";
15991
16713
  import Fuse from "fuse.js";
15992
16714
  import { jsx as jsx49, jsxs as jsxs45 } from "react/jsx-runtime";
@@ -16053,7 +16775,7 @@ function TokenSelectorSheet({
16053
16775
  useEffect28(() => {
16054
16776
  setRecentTokens(getRecentTokens());
16055
16777
  }, []);
16056
- const allOptions = useMemo8(() => {
16778
+ const allOptions = useMemo9(() => {
16057
16779
  const options = [];
16058
16780
  tokens.forEach((token) => {
16059
16781
  token.chains.forEach((chain) => {
@@ -16062,7 +16784,7 @@ function TokenSelectorSheet({
16062
16784
  });
16063
16785
  return options;
16064
16786
  }, [tokens]);
16065
- const quickSelectOptions = useMemo8(() => {
16787
+ const quickSelectOptions = useMemo9(() => {
16066
16788
  const result = [];
16067
16789
  const seen = /* @__PURE__ */ new Set();
16068
16790
  const addOption = (symbol, chainType, chainId, isRecent) => {
@@ -16094,7 +16816,7 @@ function TokenSelectorSheet({
16094
16816
  });
16095
16817
  setRecentTokens(updated);
16096
16818
  };
16097
- const fuse = useMemo8(
16819
+ const fuse = useMemo9(
16098
16820
  () => new Fuse(allOptions, {
16099
16821
  keys: [
16100
16822
  { name: "token.symbol", weight: 2 },
@@ -16107,7 +16829,7 @@ function TokenSelectorSheet({
16107
16829
  }),
16108
16830
  [allOptions]
16109
16831
  );
16110
- const filteredOptions = useMemo8(() => {
16832
+ const filteredOptions = useMemo9(() => {
16111
16833
  if (!searchQuery.trim()) return allOptions;
16112
16834
  const query = searchQuery.trim();
16113
16835
  const results = fuse.search(query);
@@ -17095,7 +17817,7 @@ function TransferCryptoSingleInput({
17095
17817
  const wallets = externalWallets?.length ? externalWallets : depositAddressResponse?.data ?? [];
17096
17818
  const loading = externalWallets?.length ? false : walletsLoading;
17097
17819
  const error = walletsError?.message ?? null;
17098
- const allAvailableChains = useMemo9(() => {
17820
+ const allAvailableChains = useMemo10(() => {
17099
17821
  const chainsMap = /* @__PURE__ */ new Map();
17100
17822
  supportedTokens.forEach((t13) => {
17101
17823
  t13.chains.forEach((c) => {
@@ -17690,7 +18412,7 @@ function TransferCryptoSingleInput({
17690
18412
  }
17691
18413
 
17692
18414
  // src/components/deposits/TransferCryptoDoubleInput.tsx
17693
- import { useState as useState37, useEffect as useEffect31, useMemo as useMemo10 } from "react";
18415
+ import { useState as useState37, useEffect as useEffect31, useMemo as useMemo11 } from "react";
17694
18416
  import {
17695
18417
  ChevronDown as ChevronDown7,
17696
18418
  ChevronUp as ChevronUp5,
@@ -17899,7 +18621,7 @@ function TransferCryptoDoubleInput({
17899
18621
  const wallets = externalWallets?.length ? externalWallets : depositAddressResponse?.data ?? [];
17900
18622
  const loading = externalWallets?.length ? false : walletsLoading;
17901
18623
  const error = walletsError?.message ?? null;
17902
- const allAvailableChains = useMemo10(() => {
18624
+ const allAvailableChains = useMemo11(() => {
17903
18625
  const chainsMap = /* @__PURE__ */ new Map();
17904
18626
  supportedTokens.forEach((t13) => {
17905
18627
  t13.chains.forEach((c) => {
@@ -20660,141 +21382,156 @@ function WalletConnect({
20660
21382
  setReceivedUsdAtSubmission(checkoutReceivedUsd ?? "0");
20661
21383
  setHasSignedTransaction(true);
20662
21384
  handleIveDeposited();
20663
- setIsConfirming(false);
20664
- transitionTo("confirming");
20665
- onSuccess?.(txHash);
20666
- } catch (err) {
20667
- const msg = err instanceof Error ? err.message : "Transaction failed";
20668
- setError(msg);
20669
- onError?.(err instanceof Error ? err : new Error(msg));
20670
- setIsConfirming(false);
20671
- }
20672
- };
20673
- if (standalone && !autoResolved) {
20674
- return /* @__PURE__ */ jsxs56("div", { style: viewTransitionStyle, children: [
20675
- /* @__PURE__ */ jsx62(
20676
- DepositHeader,
20677
- {
20678
- title: "Connect Wallet",
20679
- showBack: canGoBack,
20680
- onBack: handleBack,
20681
- onClose
20682
- }
20683
- ),
20684
- /* @__PURE__ */ jsx62("div", { className: "uf-flex uf-flex-col uf-items-center uf-justify-center uf-py-16", children: /* @__PURE__ */ jsx62(Loader210, { className: "uf-w-8 uf-h-8 uf-animate-spin", style: { color: colors2.primary } }) })
20685
- ] });
20686
- }
20687
- if (view === "select_wallet") {
20688
- return /* @__PURE__ */ jsxs56("div", { style: viewTransitionStyle, children: [
20689
- /* @__PURE__ */ jsx62(
20690
- DepositHeader,
20691
- {
20692
- title: "Connect Wallet",
20693
- showBack: canGoBack,
20694
- onBack: handleBack,
20695
- onClose
20696
- }
20697
- ),
20698
- /* @__PURE__ */ jsxs56("div", { className: "uf-pb-4", children: [
20699
- /* @__PURE__ */ jsx62(
20700
- "p",
20701
- {
20702
- className: "uf-text-sm uf-text-center uf-pb-4",
20703
- style: { color: colors2.foregroundMuted, fontFamily: fonts.regular },
20704
- children: isMobile ? "Open this page in your wallet's app to connect" : "Select a wallet to connect"
20705
- }
20706
- ),
20707
- /* @__PURE__ */ jsx62("div", { className: "uf-space-y-2", style: { maxHeight: 330, overflowY: "auto" }, children: availableWallets.map((wallet) => {
20708
- const walletPlatformAllowed = !wallet.mobileBrowsePlatforms || wallet.mobileBrowsePlatforms.includes(getMobilePlatform() ?? "");
20709
- const showOpenInApp = isMobile && !wallet.isInstalled && wallet.supportsMobileBrowse !== false && walletPlatformAllowed;
20710
- const isPending = pendingMobileWallet?.id === wallet.id;
20711
- return /* @__PURE__ */ jsxs56(
20712
- "button",
20713
- {
20714
- onClick: () => void handleWalletClick(wallet),
20715
- disabled: isWalletConnecting || !!pendingMobileWallet,
20716
- className: "uf-w-full uf-transition-colors uf-p-3 uf-flex uf-items-center uf-justify-between hover:uf-opacity-90 disabled:uf-opacity-50",
20717
- style: {
20718
- backgroundColor: components.card.backgroundColor,
20719
- borderRadius: components.card.borderRadius,
20720
- border: `${components.card.borderWidth}px solid ${components.card.borderColor}`
20721
- },
20722
- children: [
20723
- /* @__PURE__ */ jsxs56("div", { className: "uf-flex uf-items-center uf-gap-3", children: [
20724
- WALLET_ICONS3[wallet.id] ? /* @__PURE__ */ jsx62(
20725
- WalletIconWithNetwork,
20726
- {
20727
- WalletIcon: WALLET_ICONS3[wallet.id],
20728
- networks: wallet.networks,
20729
- size: 40,
20730
- className: "uf-rounded-lg"
20731
- }
20732
- ) : /* @__PURE__ */ jsx62("div", { className: "uf-w-10 uf-h-10 uf-rounded-lg uf-bg-gray-500" }),
20733
- /* @__PURE__ */ jsxs56("div", { className: "uf-flex uf-items-center uf-gap-2", children: [
20734
- /* @__PURE__ */ jsx62(
20735
- "div",
20736
- {
20737
- className: "uf-text-sm uf-font-medium",
20738
- style: { color: components.card.titleColor, fontFamily: fonts.medium },
20739
- children: wallet.name
20740
- }
20741
- ),
20742
- wallet.id === recentWalletId && /* @__PURE__ */ jsx62(
20743
- "span",
20744
- {
20745
- className: "uf-text-xs uf-px-2 uf-py-0.5 uf-rounded-full",
20746
- style: {
20747
- backgroundColor: colors2.primary + "20",
20748
- color: colors2.primary,
20749
- fontFamily: fonts.medium
20750
- },
20751
- children: "Last used"
20752
- }
20753
- )
20754
- ] })
20755
- ] }),
20756
- isPending ? /* @__PURE__ */ jsx62(
20757
- Loader210,
20758
- {
20759
- className: "uf-w-4 uf-h-4 uf-animate-spin",
20760
- style: { color: colors2.primary }
20761
- }
20762
- ) : wallet.isInstalled ? /* @__PURE__ */ jsx62(
20763
- "span",
21385
+ setIsConfirming(false);
21386
+ transitionTo("confirming");
21387
+ onSuccess?.(txHash);
21388
+ } catch (err) {
21389
+ const msg = err instanceof Error ? err.message : "Transaction failed";
21390
+ setError(msg);
21391
+ onError?.(err instanceof Error ? err : new Error(msg));
21392
+ setIsConfirming(false);
21393
+ }
21394
+ };
21395
+ if (standalone && !autoResolved) {
21396
+ return /* @__PURE__ */ jsxs56("div", { style: viewTransitionStyle, children: [
21397
+ /* @__PURE__ */ jsx62(
21398
+ DepositHeader,
21399
+ {
21400
+ title: "Connect Wallet",
21401
+ showBack: canGoBack,
21402
+ onBack: handleBack,
21403
+ onClose
21404
+ }
21405
+ ),
21406
+ /* @__PURE__ */ jsx62("div", { className: "uf-flex uf-flex-col uf-items-center uf-justify-center uf-py-16", children: /* @__PURE__ */ jsx62(Loader210, { className: "uf-w-8 uf-h-8 uf-animate-spin", style: { color: colors2.primary } }) })
21407
+ ] });
21408
+ }
21409
+ if (view === "select_wallet") {
21410
+ return (
21411
+ // Mobile: flex column that fills the full-height sheet so the wallet list
21412
+ // scrolls and the footer pins to the bottom. Desktop (sm:): content-sized.
21413
+ /* @__PURE__ */ jsxs56(
21414
+ "div",
21415
+ {
21416
+ style: viewTransitionStyle,
21417
+ className: "uf-flex uf-min-h-0 uf-flex-1 uf-flex-col sm:uf-block",
21418
+ children: [
21419
+ /* @__PURE__ */ jsx62(
21420
+ DepositHeader,
21421
+ {
21422
+ title: "Connect Wallet",
21423
+ showBack: canGoBack,
21424
+ onBack: handleBack,
21425
+ onClose
21426
+ }
21427
+ ),
21428
+ /* @__PURE__ */ jsxs56("div", { className: "uf-pb-4 uf-flex uf-min-h-0 uf-flex-1 uf-flex-col sm:uf-block", children: [
21429
+ /* @__PURE__ */ jsx62(
21430
+ "p",
21431
+ {
21432
+ className: "uf-text-sm uf-text-center uf-pb-4",
21433
+ style: { color: colors2.foregroundMuted, fontFamily: fonts.regular },
21434
+ children: isMobile ? "Open this page in your wallet's app to connect" : "Select a wallet to connect"
21435
+ }
21436
+ ),
21437
+ /* @__PURE__ */ jsx62("div", { className: "uf-space-y-2 uf-min-h-0 uf-flex-1 uf-overflow-y-auto sm:uf-flex-none sm:uf-max-h-[330px] [scrollbar-width:none] [&::-webkit-scrollbar]:uf-hidden", children: availableWallets.filter((wallet) => {
21438
+ if (!isMobile || wallet.isInstalled) return true;
21439
+ const platformAllowed = !wallet.mobileBrowsePlatforms || wallet.mobileBrowsePlatforms.includes(getMobilePlatform() ?? "");
21440
+ return wallet.supportsMobileBrowse !== false && platformAllowed;
21441
+ }).map((wallet) => {
21442
+ const walletPlatformAllowed = !wallet.mobileBrowsePlatforms || wallet.mobileBrowsePlatforms.includes(getMobilePlatform() ?? "");
21443
+ const showOpenInApp = isMobile && !wallet.isInstalled && wallet.supportsMobileBrowse !== false && walletPlatformAllowed;
21444
+ const isPending = pendingMobileWallet?.id === wallet.id;
21445
+ return /* @__PURE__ */ jsxs56(
21446
+ "button",
20764
21447
  {
20765
- className: "uf-text-xs uf-px-2 uf-py-1 uf-rounded-full",
21448
+ onClick: () => void handleWalletClick(wallet),
21449
+ disabled: isWalletConnecting || !!pendingMobileWallet,
21450
+ className: "uf-w-full uf-transition-colors uf-p-3 uf-flex uf-items-center uf-justify-between hover:uf-opacity-90 disabled:uf-opacity-50",
20766
21451
  style: {
20767
- backgroundColor: colors2.primary + "20",
20768
- color: colors2.primary,
20769
- fontFamily: fonts.medium
21452
+ backgroundColor: components.card.backgroundColor,
21453
+ borderRadius: components.card.borderRadius,
21454
+ border: `${components.card.borderWidth}px solid ${components.card.borderColor}`
20770
21455
  },
20771
- children: "Detected"
20772
- }
20773
- ) : /* @__PURE__ */ jsxs56("div", { className: "uf-flex uf-items-center uf-gap-1", children: [
20774
- /* @__PURE__ */ jsx62(
20775
- "span",
20776
- {
20777
- className: "uf-text-xs",
20778
- style: { color: colors2.foregroundMuted, fontFamily: fonts.regular },
20779
- children: showOpenInApp ? "Open" : "Install"
20780
- }
20781
- ),
20782
- /* @__PURE__ */ jsx62(
20783
- ExternalLink4,
20784
- {
20785
- className: "uf-w-3 uf-h-3",
20786
- style: { color: colors2.foregroundMuted }
20787
- }
20788
- )
20789
- ] })
20790
- ]
20791
- },
20792
- wallet.id
20793
- );
20794
- }) }),
20795
- walletError && /* @__PURE__ */ jsx62("div", { className: "uf-text-center uf-text-sm uf-mt-4 uf-px-4", style: { color: "#ef4444" }, children: walletError })
20796
- ] })
20797
- ] });
21456
+ children: [
21457
+ /* @__PURE__ */ jsxs56("div", { className: "uf-flex uf-items-center uf-gap-3", children: [
21458
+ WALLET_ICONS3[wallet.id] ? /* @__PURE__ */ jsx62(
21459
+ WalletIconWithNetwork,
21460
+ {
21461
+ WalletIcon: WALLET_ICONS3[wallet.id],
21462
+ networks: wallet.networks,
21463
+ size: 40,
21464
+ className: "uf-rounded-lg"
21465
+ }
21466
+ ) : /* @__PURE__ */ jsx62("div", { className: "uf-w-10 uf-h-10 uf-rounded-lg uf-bg-gray-500" }),
21467
+ /* @__PURE__ */ jsxs56("div", { className: "uf-flex uf-items-center uf-gap-2", children: [
21468
+ /* @__PURE__ */ jsx62(
21469
+ "div",
21470
+ {
21471
+ className: "uf-text-sm uf-font-medium",
21472
+ style: { color: components.card.titleColor, fontFamily: fonts.medium },
21473
+ children: wallet.name
21474
+ }
21475
+ ),
21476
+ wallet.id === recentWalletId && /* @__PURE__ */ jsx62(
21477
+ "span",
21478
+ {
21479
+ className: "uf-text-xs uf-px-2 uf-py-0.5 uf-rounded-full",
21480
+ style: {
21481
+ backgroundColor: colors2.primary + "20",
21482
+ color: colors2.primary,
21483
+ fontFamily: fonts.medium
21484
+ },
21485
+ children: "Last used"
21486
+ }
21487
+ )
21488
+ ] })
21489
+ ] }),
21490
+ isPending ? /* @__PURE__ */ jsx62(
21491
+ Loader210,
21492
+ {
21493
+ className: "uf-w-4 uf-h-4 uf-animate-spin",
21494
+ style: { color: colors2.primary }
21495
+ }
21496
+ ) : wallet.isInstalled ? /* @__PURE__ */ jsx62(
21497
+ "span",
21498
+ {
21499
+ className: "uf-text-xs uf-px-2 uf-py-1 uf-rounded-full",
21500
+ style: {
21501
+ backgroundColor: colors2.primary + "20",
21502
+ color: colors2.primary,
21503
+ fontFamily: fonts.medium
21504
+ },
21505
+ children: "Detected"
21506
+ }
21507
+ ) : /* @__PURE__ */ jsxs56("div", { className: "uf-flex uf-items-center uf-gap-1", children: [
21508
+ /* @__PURE__ */ jsx62(
21509
+ "span",
21510
+ {
21511
+ className: "uf-text-xs",
21512
+ style: { color: colors2.foregroundMuted, fontFamily: fonts.regular },
21513
+ children: showOpenInApp ? "Open" : "Install"
21514
+ }
21515
+ ),
21516
+ /* @__PURE__ */ jsx62(
21517
+ ExternalLink4,
21518
+ {
21519
+ className: "uf-w-3 uf-h-3",
21520
+ style: { color: colors2.foregroundMuted }
21521
+ }
21522
+ )
21523
+ ] })
21524
+ ]
21525
+ },
21526
+ wallet.id
21527
+ );
21528
+ }) }),
21529
+ walletError && /* @__PURE__ */ jsx62("div", { className: "uf-text-center uf-text-sm uf-mt-4 uf-px-4", style: { color: "#ef4444" }, children: walletError })
21530
+ ] })
21531
+ ]
21532
+ }
21533
+ )
21534
+ );
20798
21535
  }
20799
21536
  const preConnectAccent = selectedWalletDef ? getWalletBrandColor(selectedWalletDef.id, mode) : void 0;
20800
21537
  const preConnectFg = preConnectAccent ? getContrastingTextColor(preConnectAccent) : void 0;
@@ -21186,11 +21923,12 @@ function DepositModal({
21186
21923
  applePayTitle = "Pay with Apple Pay",
21187
21924
  applePaySubTitle = "Instant",
21188
21925
  enableBankTransfer,
21189
- enableStripeLink = false,
21190
- email: emailProp,
21926
+ // No default: left undefined so the backend `stripe_link.enabled` can govern
21927
+ // (via the `??` chain in showStripeLink) once a dashboard toggle exists.
21928
+ enableStripeLink,
21929
+ userEmail,
21191
21930
  hideDepositFlowInfo = false,
21192
21931
  hideDisplayDescription = false,
21193
- externalUserEmail,
21194
21932
  onDepositSuccess,
21195
21933
  onDepositError,
21196
21934
  onEvent,
@@ -21222,7 +21960,7 @@ function DepositModal({
21222
21960
  (method) => onDepositError ? (error) => onDepositError({ ...error, method }) : void 0,
21223
21961
  [onDepositError]
21224
21962
  );
21225
- const effectiveInitialScreen = useMemo12(() => {
21963
+ const effectiveInitialScreen = useMemo13(() => {
21226
21964
  const s = initialScreen ?? "main";
21227
21965
  if (s === "tracker" && hideDepositTracker === true) return "main";
21228
21966
  if (s === "cashapp" && enableCashApp === false) return "main";
@@ -21268,14 +22006,18 @@ function DepositModal({
21268
22006
  const [allExecutions, setAllExecutions] = useState40([]);
21269
22007
  const [selectedExecution, setSelectedExecution] = useState40(null);
21270
22008
  const [depositExecutions, setDepositExecutions] = useState40([]);
22009
+ const { userIpInfo, isLoading: isLoadingIp } = useUserIp();
21271
22010
  const { projectConfig } = useProjectConfig({
21272
22011
  publishableKey,
21273
- enabled: open
22012
+ enabled: open && !isLoadingIp,
22013
+ countryCode: userIpInfo?.alpha2,
22014
+ subdivisionCode: userIpInfo?.subdivisionCode ?? void 0
21274
22015
  });
21275
22016
  const showTransferCrypto = enableTransferCrypto ?? projectConfig?.transfer_crypto?.enabled ?? true;
21276
22017
  const showConnectWallet = enableConnectWallet ?? projectConfig?.connect_wallet?.enabled ?? true;
21277
22018
  const showPayWithExchange = enablePayWithExchange ?? projectConfig?.pay_with_exchange?.enabled ?? true;
21278
22019
  const showFiatOnramp = !projectConfig?.fiat_onramp?.is_hidden && (enableFiatOnramp ?? projectConfig?.fiat_onramp?.enabled ?? true);
22020
+ const showStripeLink = !projectConfig?.stripe_link?.is_hidden && (enableStripeLink ?? projectConfig?.stripe_link?.enabled ?? false);
21279
22021
  const showConnectExchange = enableConnectExchange ?? projectConfig?.connect_exchange?.enabled ?? true;
21280
22022
  const showCashApp = enableCashApp ?? projectConfig?.cash_app?.enabled ?? true;
21281
22023
  const showApplePay = enableApplePay ?? projectConfig?.apple_pay?.enabled ?? true;
@@ -21421,7 +22163,6 @@ function DepositModal({
21421
22163
  publishableKey,
21422
22164
  enabled: open && showPayWithExchange
21423
22165
  });
21424
- const { userIpInfo, isLoading: isLoadingIp } = useUserIp();
21425
22166
  const { providers: bankTransferProviders, isLoading: bankTransferProvidersLoading } = useBankTransferProviders({
21426
22167
  publishableKey,
21427
22168
  enabled: open && !!userIpInfo?.alpha2,
@@ -21599,6 +22340,12 @@ function DepositModal({
21599
22340
  const [cashAppView, setCashAppView] = useState40("amount");
21600
22341
  const [stripeLinkStep, setStripeLinkStep] = useState40("amount");
21601
22342
  const stripeLinkBackRef = useRef12(null);
22343
+ useEffect34(() => {
22344
+ if (view === "stripe_link" && !showStripeLink && effectiveInitialScreen === "main") {
22345
+ setView("main");
22346
+ setStripeLinkStep("amount");
22347
+ }
22348
+ }, [view, showStripeLink, effectiveInitialScreen]);
21602
22349
  const [cashAppAmount, setCashAppAmount] = useState40("");
21603
22350
  const [applePayView, setApplePayView] = useState40("email_input");
21604
22351
  const applePayHandleRef = useRef12(null);
@@ -21621,11 +22368,18 @@ function DepositModal({
21621
22368
  return "Success";
21622
22369
  case "guest_limit_reached":
21623
22370
  return "Limit reached";
22371
+ case "limit_upgrade_intro":
22372
+ case "limit_upgrade_failed":
22373
+ return "Limit reached";
22374
+ case "limit_upgrade_form":
22375
+ return "Verify identity";
22376
+ case "limit_upgrade_submitting":
22377
+ return "Verifying";
21624
22378
  default:
21625
22379
  return "Pay with Apple Pay";
21626
22380
  }
21627
22381
  })();
21628
- const applePayShowBack = sessionOpenedFromMenu || applePayView === "phone_input" || applePayView === "email_otp" || applePayView === "phone_otp" || applePayView === "submitting_session" || applePayView === "checking_deposit";
22382
+ const applePayShowBack = sessionOpenedFromMenu || applePayView === "phone_input" || applePayView === "email_otp" || applePayView === "phone_otp" || applePayView === "submitting_session" || applePayView === "checking_deposit" || applePayView === "limit_upgrade_intro" || applePayView === "limit_upgrade_form" || applePayView === "limit_upgrade_failed";
21629
22383
  const handleBack = () => {
21630
22384
  if (view === "card" && cardView === "quotes") {
21631
22385
  setCardView("amount");
@@ -21816,7 +22570,7 @@ function DepositModal({
21816
22570
  },
21817
22571
  "cashapp"
21818
22572
  ) : null;
21819
- const stripeLinkMenuButton = enableStripeLink ? /* @__PURE__ */ jsx63(
22573
+ const stripeLinkMenuButton = showStripeLink ? /* @__PURE__ */ jsx63(
21820
22574
  StripeLinkButton,
21821
22575
  {
21822
22576
  onClick: () => setView("stripe_link"),
@@ -21864,11 +22618,13 @@ function DepositModal({
21864
22618
  connectExchangeMenuButton
21865
22619
  ].filter(Boolean);
21866
22620
  const cashMenuButtons = [
22621
+ // Stripe "Pay with Link" is intentionally listed first so it always sits
22622
+ // above "Deposit with Card".
22623
+ stripeLinkMenuButton,
21867
22624
  depositWithCardMenuButton,
21868
22625
  applePayMenuButton,
21869
22626
  cashAppMenuButton,
21870
- bankTransferMenuButton,
21871
- stripeLinkMenuButton
22627
+ bankTransferMenuButton
21872
22628
  ].filter(Boolean);
21873
22629
  const depositTabs = [
21874
22630
  { id: "crypto", label: "Use Crypto", icon: Bitcoin, buttons: cryptoMenuButtons },
@@ -21963,13 +22719,13 @@ function DepositModal({
21963
22719
  const stackedOptions = [
21964
22720
  transferCryptoMenuButton,
21965
22721
  connectWalletMenuButton,
22722
+ stripeLinkMenuButton,
21966
22723
  depositWithCardMenuButton,
21967
22724
  applePayMenuButton,
21968
22725
  payWithExchangeMenuButton,
21969
22726
  connectExchangeMenuButton,
21970
22727
  cashAppMenuButton,
21971
22728
  bankTransferMenuButton,
21972
- stripeLinkMenuButton,
21973
22729
  depositTrackerMenuButton
21974
22730
  ].filter(Boolean);
21975
22731
  return renderScrollableOptions(stackedOptions);
@@ -21985,413 +22741,452 @@ function DepositModal({
21985
22741
  {
21986
22742
  ref: hideOverlay ? containerCallbackRef : void 0,
21987
22743
  hideOverlay,
21988
- className: `sm:uf-max-w-[400px] uf-border-secondary uf-text-foreground uf-gap-0 [&>button]:uf-hidden ${hideOverlay ? `uf-p-6 uf-overflow-hidden ${themeClass}` : `uf-p-0 uf-overflow-visible ${view === "main" ? "!uf-top-auto !uf-h-auto !uf-max-h-[85vh] sm:!uf-max-h-none sm:!uf-top-[50%]" : "!uf-top-0 !uf-h-full sm:!uf-h-auto sm:!uf-top-[50%]"} ${themeClass}`}`,
22744
+ className: `sm:uf-max-w-[400px] uf-border-secondary uf-text-foreground uf-gap-0 [&>button]:uf-hidden ${hideOverlay ? `uf-p-6 uf-overflow-hidden ${themeClass}` : `uf-p-0 uf-overflow-visible ${view === "main" ? "!uf-top-auto !uf-h-auto !uf-max-h-[85vh] sm:!uf-max-h-none sm:!uf-top-[50%]" : "!uf-top-0 !uf-h-full sm:!uf-h-auto sm:!uf-top-[50%]"} ${// wallet_connect fills the full-height mobile sheet. DialogContent
22745
+ // is a grid, and its single auto row only *grows* to fill free
22746
+ // space (align-content: stretch) — it never shrinks below content,
22747
+ // so a long wallet list would balloon the row past the viewport and
22748
+ // push the footer off-screen instead of scrolling. Clamp the row to
22749
+ // the modal height with minmax(0,1fr) so the inner overflow-y-auto
22750
+ // list scrolls with the footer pinned. Reset to content-sized on
22751
+ // desktop (sm:) where the modal is auto-height and centered.
22752
+ view === "wallet_connect" ? "[grid-template-rows:minmax(0,1fr)] sm:[grid-template-rows:none]" : ""} ${themeClass}`}`,
21989
22753
  style: { backgroundColor: colors2.background },
21990
22754
  onPointerDownOutside: (e) => e.preventDefault(),
21991
22755
  onInteractOutside: (e) => e.preventDefault(),
21992
22756
  children: [
21993
22757
  /* @__PURE__ */ jsx63(DialogTitle, { className: "uf-sr-only", children: modalTitle || "Deposit" }),
21994
- /* @__PURE__ */ jsx63(ThemeStyleInjector, { children: view === "main" ? /* @__PURE__ */ jsxs57("div", { className: "uf-flex uf-flex-col uf-min-h-0 uf-max-h-full", children: [
21995
- /* @__PURE__ */ jsx63("div", { className: "uf-flex-shrink-0", children: /* @__PURE__ */ jsx63(
21996
- DepositHeader,
21997
- {
21998
- title: modalTitle || "Deposit",
21999
- showClose: !hideOverlay,
22000
- onClose: handleClose,
22001
- showBalance: showBalanceHeader,
22002
- balanceAddress: recipientAddress,
22003
- balanceChainType: destinationChainType === "ethereum" || destinationChainType === "solana" || destinationChainType === "bitcoin" || destinationChainType === "n1" ? destinationChainType : void 0,
22004
- balanceChainId: destinationChainId,
22005
- balanceTokenAddress: destinationTokenAddress,
22006
- projectName: projectConfig?.project_name,
22007
- publishableKey
22008
- }
22009
- ) }),
22010
- renderMainMenuBody(),
22011
- /* @__PURE__ */ jsx63("div", { className: "uf-flex-shrink-0", children: depositPoweredByFooter })
22012
- ] }) : view === "transfer" ? /* @__PURE__ */ jsxs57(Fragment14, { children: [
22013
- /* @__PURE__ */ jsx63(
22014
- DepositHeader,
22015
- {
22016
- title: transferCryptoTitle,
22017
- showBack: showBackTransfer,
22018
- onBack: handleBack,
22019
- onClose: handleClose,
22020
- showBalance: showBalanceHeader,
22021
- balanceAddress: recipientAddress,
22022
- balanceChainType: destinationChainType === "ethereum" || destinationChainType === "solana" || destinationChainType === "bitcoin" || destinationChainType === "n1" ? destinationChainType : void 0,
22023
- balanceChainId: destinationChainId,
22024
- balanceTokenAddress: destinationTokenAddress,
22025
- projectName: projectConfig?.project_name,
22026
- publishableKey
22027
- }
22028
- ),
22029
- /* @__PURE__ */ jsxs57("div", { className: "uf-flex uf-flex-col uf-gap-1.5", children: [
22030
- standaloneNeedsDepositPrereq && depositPrerequisiteBody !== null ? /* @__PURE__ */ jsx63("div", { className: "uf-pb-4 uf-space-y-3", children: depositPrerequisiteBody }) : transferInputVariant === "single_input" ? /* @__PURE__ */ jsx63(
22031
- TransferCryptoSingleInput,
22032
- {
22033
- userId,
22034
- publishableKey,
22035
- recipientAddress,
22036
- destinationChainType,
22037
- destinationChainId,
22038
- destinationTokenAddress,
22039
- defaultSourceChainType,
22040
- defaultSourceChainId,
22041
- defaultSourceTokenAddress,
22042
- defaultSourceSymbol,
22043
- depositConfirmationMode,
22044
- onExecutionsChange: setDepositExecutions,
22045
- onDepositSuccess: onDepositSuccessFor("transfer"),
22046
- onDepositError: onDepositErrorFor("transfer"),
22047
- wallets
22048
- }
22049
- ) : /* @__PURE__ */ jsx63(
22050
- TransferCryptoDoubleInput,
22051
- {
22052
- userId,
22053
- publishableKey,
22054
- recipientAddress,
22055
- destinationChainType,
22056
- destinationChainId,
22057
- destinationTokenAddress,
22058
- defaultSourceChainType,
22059
- defaultSourceChainId,
22060
- defaultSourceTokenAddress,
22061
- defaultSourceSymbol,
22062
- depositConfirmationMode,
22063
- onExecutionsChange: setDepositExecutions,
22064
- onDepositSuccess: onDepositSuccessFor("transfer"),
22065
- onDepositError: onDepositErrorFor("transfer"),
22066
- wallets
22067
- }
22068
- ),
22069
- depositPoweredByFooter
22070
- ] })
22071
- ] }) : view === "tracker" ? /* @__PURE__ */ jsxs57(Fragment14, { children: [
22072
- /* @__PURE__ */ jsx63(
22073
- DepositHeader,
22074
- {
22075
- title: selectedExecution ? "Deposit Details" : depositTrackerTitle,
22076
- showBack: showBackTracker,
22077
- onBack: handleBack,
22078
- onClose: handleClose
22079
- }
22080
- ),
22081
- /* @__PURE__ */ jsxs57("div", { className: "uf-flex uf-flex-col uf-gap-1.5", children: [
22082
- /* @__PURE__ */ jsx63("div", { className: "uf-h-[460px] uf-overflow-y-auto [scrollbar-width:none] [&::-webkit-scrollbar]:uf-hidden", children: selectedExecution ? /* @__PURE__ */ jsx63(DepositDetailContent, { execution: selectedExecution }) : /* @__PURE__ */ jsx63("div", { className: "uf-space-y-2 uf-pb-8", children: allExecutions.length === 0 ? /* @__PURE__ */ jsx63("div", { className: "uf-py-8 uf-px-4 uf-text-center", children: /* @__PURE__ */ jsx63(
22083
- "div",
22084
- {
22085
- className: "uf-text-sm",
22086
- style: {
22087
- color: components.container.subtitleColor,
22088
- fontFamily: fonts.regular
22089
- },
22090
- children: "No deposits yet"
22091
- }
22092
- ) }) : allExecutions.map((execution) => /* @__PURE__ */ jsx63(
22093
- DepositExecutionItem,
22094
- {
22095
- execution,
22096
- onClick: () => setSelectedExecution(execution)
22097
- },
22098
- execution.id
22099
- )) }) }),
22100
- depositPoweredByFooter
22101
- ] })
22102
- ] }) : view === "card" ? /* @__PURE__ */ jsxs57(Fragment14, { children: [
22103
- /* @__PURE__ */ jsx63(
22104
- DepositHeader,
22105
- {
22106
- title: cardView === "quotes" ? t8.quotes : depositWithCardTitle,
22107
- showBack: showBackCard,
22108
- onBack: handleBack,
22109
- onClose: handleClose,
22110
- badge: cardView === "quotes" ? { count: quotesCount } : void 0,
22111
- showBalance: showBalanceHeader,
22112
- balanceAddress: recipientAddress,
22113
- balanceChainType: destinationChainType === "ethereum" || destinationChainType === "solana" || destinationChainType === "bitcoin" || destinationChainType === "n1" ? destinationChainType : void 0,
22114
- balanceChainId: destinationChainId,
22115
- balanceTokenAddress: destinationTokenAddress,
22116
- projectName: projectConfig?.project_name,
22117
- publishableKey
22118
- }
22119
- ),
22120
- /* @__PURE__ */ jsxs57("div", { className: "uf-flex uf-flex-col uf-gap-1.5", children: [
22121
- standaloneNeedsDepositPrereq && depositPrerequisiteBody !== null ? /* @__PURE__ */ jsx63("div", { className: "uf-pb-4 uf-space-y-3", children: depositPrerequisiteBody }) : !showFiatOnramp ? (
22122
- // Fiat on-ramp resolved hidden/disabled after a direct open
22123
- // (e.g. platform `is_hidden` for a Stripe Link-only project).
22124
- // Show a geo-restriction screen rather than the card UI so the
22125
- // hard-hide is honoured without a flash of "Pay with Card".
22126
- /* @__PURE__ */ jsx63(GeoRestrictionScreen, { methodName: "Card" })
22127
- ) : /* @__PURE__ */ jsx63(
22128
- BuyWithCard,
22129
- {
22130
- userId,
22131
- publishableKey,
22132
- view: cardView,
22133
- onViewChange: handleCardViewChange,
22134
- destinationTokenSymbol,
22135
- recipientAddress,
22136
- destinationChainType,
22137
- destinationChainId,
22138
- destinationTokenAddress,
22139
- onDepositSuccess: onDepositSuccessFor("card"),
22140
- onDepositError: onDepositErrorFor("card"),
22141
- onEvent,
22142
- themeClass,
22143
- wallets,
22144
- assetCdnUrl: projectConfig?.asset_cdn_url,
22145
- hideDepositFlowInfo,
22146
- hideDisplayDescription
22147
- }
22148
- ),
22149
- depositPoweredByFooter
22150
- ] })
22151
- ] }) : view === "exchange" ? /* @__PURE__ */ jsxs57(Fragment14, { children: [
22152
- /* @__PURE__ */ jsx63(
22153
- DepositHeader,
22154
- {
22155
- title: payWithExchangeTitle,
22156
- showBack: exchangeView === "pending" || sessionOpenedFromMenu,
22157
- onBack: handleBack,
22158
- onClose: handleClose
22159
- }
22160
- ),
22161
- /* @__PURE__ */ jsxs57("div", { className: "uf-flex uf-flex-col uf-gap-1.5", children: [
22162
- /* @__PURE__ */ jsx63(
22163
- PayWithExchange,
22164
- {
22165
- userId,
22166
- publishableKey,
22167
- exchanges,
22168
- view: exchangeView,
22169
- onViewChange: setExchangeView,
22170
- destinationTokenSymbol,
22171
- recipientAddress,
22172
- destinationChainType,
22173
- destinationChainId,
22174
- destinationTokenAddress,
22175
- onDepositSuccess: onDepositSuccessFor("pay_with_exchange"),
22176
- onDepositError: onDepositErrorFor("pay_with_exchange"),
22177
- wallets,
22178
- defaultToken: defaultToken ?? null
22179
- }
22180
- ),
22181
- depositPoweredByFooter
22182
- ] })
22183
- ] }) : view === "coinbase_connect" ? /* @__PURE__ */ jsxs57("div", { className: "uf-flex uf-flex-col uf-gap-1.5", children: [
22184
- /* @__PURE__ */ jsx63(
22185
- CoinbaseConnect,
22186
- {
22187
- publishableKey,
22188
- userId,
22189
- wallets,
22190
- recipientAddress,
22191
- destinationTokenAddress: destinationTokenAddress ?? "",
22192
- destinationChainId: destinationChainId ?? "",
22193
- destinationChainType: destinationChainType ?? "",
22194
- onDepositSuccess: onDepositSuccessFor("exchange_connect"),
22195
- onDepositError: onDepositErrorFor("exchange_connect"),
22196
- onTransferError: (error) => {
22197
- onDepositErrorFor("exchange_connect")?.({
22198
- message: error.message,
22199
- error
22200
- });
22201
- },
22202
- onBack: handleBack,
22203
- onClose: handleClose,
22204
- onDisconnect: handleExchangeDisconnect,
22205
- skipToHoldings: coinbaseSkipToHoldings,
22206
- canGoBack: sessionOpenedFromMenu,
22207
- onExecutionsChange: setDepositExecutions,
22208
- defaultSourceChainType,
22209
- defaultSourceChainId,
22210
- defaultSourceTokenAddress,
22211
- defaultSourceSymbol
22212
- }
22213
- ),
22214
- depositPoweredByFooter
22215
- ] }) : view === "wallet_connect" ? /* @__PURE__ */ jsxs57("div", { className: "uf-flex uf-flex-col uf-gap-1.5", children: [
22216
- /* @__PURE__ */ jsx63(
22217
- WalletConnect,
22218
- {
22219
- walletInfo: browserWalletInfo ?? void 0,
22220
- depositWallet: browserWalletInfo?.depositWallet ?? void 0,
22221
- wallets,
22222
- userId,
22223
- publishableKey,
22224
- assetCdnUrl: projectConfig?.asset_cdn_url,
22225
- projectName: projectConfig?.project_name,
22226
- onError: (error) => {
22227
- onDepositErrorFor("wallet_connect")?.({
22228
- message: error.message,
22229
- error
22230
- });
22231
- },
22232
- onDepositSuccess: onDepositSuccessFor("wallet_connect"),
22233
- onDepositError: onDepositErrorFor("wallet_connect"),
22234
- amountQuickSelect: browserWalletAmountQuickSelect,
22235
- onWalletDisconnect: handleWalletDisconnect,
22236
- onWalletConnected: (info, dw) => {
22237
- setBrowserWalletInfo({ ...info, depositWallet: dw });
22238
- setStoredWalletState(info.type);
22239
- setBrowserWalletChainType(dw.chain_type === "solana" ? "solana" : "ethereum");
22240
- },
22241
- onBack: handleBack,
22242
- onClose: handleClose,
22243
- defaultSourceChainType,
22244
- defaultSourceChainId,
22245
- defaultSourceTokenAddress,
22246
- defaultSourceSymbol,
22247
- canGoBack: sessionOpenedFromMenu,
22248
- depositWalletsLoading: walletsLoading
22249
- }
22250
- ),
22251
- depositPoweredByFooter
22252
- ] }) : view === "bank_transfer" ? /* @__PURE__ */ jsxs57(Fragment14, { children: [
22253
- /* @__PURE__ */ jsx63(
22254
- DepositHeader,
22255
- {
22256
- title: t8.bankTransfer.title,
22257
- showBack: bankTransferView !== "providers" || sessionOpenedFromMenu,
22258
- onBack: handleBack,
22259
- onClose: handleClose
22260
- }
22261
- ),
22262
- /* @__PURE__ */ jsxs57("div", { className: "uf-flex uf-flex-col uf-gap-1.5", children: [
22263
- bankTransferProvidersLoading ? /* @__PURE__ */ jsx63(SkeletonButton, { variant: "with-icons" }) : !hasEnabledBankTransferProvider ? /* @__PURE__ */ jsx63(GeoRestrictionScreen, { methodName: "Bank Transfer" }) : /* @__PURE__ */ jsx63(
22264
- BankTransfer,
22265
- {
22266
- userId,
22267
- publishableKey,
22268
- view: bankTransferView,
22269
- onViewChange: setBankTransferView,
22270
- recipientAddress,
22271
- destinationChainType,
22272
- destinationChainId,
22273
- destinationTokenAddress,
22274
- destinationTokenSymbol,
22275
- wallets,
22276
- defaultToken: defaultToken ?? null,
22277
- assetCdnUrl: projectConfig?.asset_cdn_url,
22278
- onEvent,
22279
- onDepositSuccess,
22280
- onDepositError
22281
- }
22282
- ),
22283
- depositPoweredByFooter
22284
- ] })
22285
- ] }) : view === "stripe_link" ? /* @__PURE__ */ jsxs57(Fragment14, { children: [
22286
- /* @__PURE__ */ jsx63(
22287
- DepositHeader,
22288
- {
22289
- title: "Deposit with Link",
22290
- showBack: stripeLinkStep !== "checkout" && stripeLinkStep !== "success",
22291
- onBack: handleBack,
22292
- showClose: stripeLinkStep !== "checkout",
22293
- onClose: handleClose
22294
- }
22295
- ),
22296
- /* @__PURE__ */ jsxs57("div", { className: "uf-flex uf-flex-col uf-gap-1.5", children: [
22297
- /* @__PURE__ */ jsx63(
22298
- PayWithStripeLink,
22299
- {
22300
- userId,
22301
- publishableKey,
22302
- recipientAddress,
22303
- destinationChainType,
22304
- destinationChainId,
22305
- destinationTokenAddress,
22306
- wallets,
22307
- email: emailProp,
22308
- iconUrl: projectConfig?.asset_cdn_url ? `${projectConfig.asset_cdn_url}/api/public/icons/onramps/svg/link.svg` : void 0,
22309
- step: stripeLinkStep,
22310
- onStepChange: setStripeLinkStep,
22311
- backHandlerRef: stripeLinkBackRef,
22312
- onDepositSuccess,
22313
- onDepositError
22314
- }
22315
- ),
22316
- depositPoweredByFooter
22317
- ] })
22318
- ] }) : view === "cashapp" ? /* @__PURE__ */ jsxs57(Fragment14, { children: [
22319
- /* @__PURE__ */ jsx63(
22320
- DepositHeader,
22321
- {
22322
- title: cashAppView !== "amount" && cashAppAmount ? `Pay $${cashAppAmount} via Cash App` : "Pay with Cash App",
22323
- showBack: cashAppView !== "amount" || sessionOpenedFromMenu,
22324
- onBack: handleBack,
22325
- onClose: handleClose
22326
- }
22327
- ),
22328
- /* @__PURE__ */ jsxs57("div", { className: "uf-flex uf-flex-col uf-gap-1.5", children: [
22329
- /* @__PURE__ */ jsx63(
22330
- PayWithCashApp,
22331
- {
22332
- userId,
22333
- publishableKey,
22334
- recipientAddress,
22335
- destinationChainType,
22336
- destinationChainId,
22337
- destinationTokenAddress,
22338
- cashAppIconUrl: projectConfig?.asset_cdn_url ? `${projectConfig.asset_cdn_url}/api/public/icons/onramps/svg/cashapp.svg` : void 0,
22339
- view: cashAppView,
22340
- onViewChange: setCashAppView,
22341
- onAmountChange: setCashAppAmount,
22342
- onEvent,
22343
- onDepositSuccess: onDepositSuccessFor("cashapp"),
22344
- onDepositError: onDepositErrorFor("cashapp"),
22345
- wallets
22346
- }
22347
- ),
22348
- depositPoweredByFooter
22349
- ] })
22350
- ] }) : view === "apple_pay" ? /* @__PURE__ */ jsxs57(Fragment14, { children: [
22351
- /* @__PURE__ */ jsx63(
22352
- DepositHeader,
22353
- {
22354
- title: applePayHeaderTitle,
22355
- showBack: applePayShowBack,
22356
- onBack: () => {
22357
- const handled = applePayHandleRef.current?.requestBack() ?? false;
22358
- if (!handled) handleBack();
22359
- },
22360
- onClose: handleClose
22361
- }
22362
- ),
22363
- /* @__PURE__ */ jsxs57("div", { className: "uf-flex uf-flex-col uf-gap-1.5", children: [
22364
- applePayProvidersLoading ? /* @__PURE__ */ jsx63(SkeletonButton, { variant: "with-icons" }) : !hasEnabledApplePayProvider ? /* @__PURE__ */ jsx63(GeoRestrictionScreen, { methodName: "Apple Pay" }) : /* @__PURE__ */ jsx63(
22365
- BuyWithApplePay,
22366
- {
22367
- ref: applePayHandleRef,
22368
- userId,
22369
- publishableKey,
22370
- recipientAddress,
22371
- destinationChainType,
22372
- destinationChainId,
22373
- destinationTokenAddress,
22374
- destinationTokenSymbol,
22375
- externalUserEmail,
22376
- enableApplePay,
22377
- wallets,
22378
- onViewChange: setApplePayView,
22379
- onEvent,
22380
- onDepositSuccess: onDepositSuccessFor("apple_pay"),
22381
- onDepositError: onDepositErrorFor("apple_pay"),
22382
- exitLabel: sessionOpenedFromMenu ? "Return" : "Close",
22383
- onExit: () => {
22384
- if (sessionOpenedFromMenu) {
22385
- setView("main");
22386
- } else {
22387
- handleClose();
22758
+ /* @__PURE__ */ jsx63(
22759
+ ThemeStyleInjector,
22760
+ {
22761
+ className: view === "wallet_connect" ? "uf-flex uf-min-h-0 uf-flex-col" : void 0,
22762
+ children: view === "main" ? /* @__PURE__ */ jsxs57("div", { className: "uf-flex uf-flex-col uf-min-h-0 uf-max-h-full", children: [
22763
+ /* @__PURE__ */ jsx63("div", { className: "uf-flex-shrink-0", children: /* @__PURE__ */ jsx63(
22764
+ DepositHeader,
22765
+ {
22766
+ title: modalTitle || "Deposit",
22767
+ showClose: !hideOverlay,
22768
+ onClose: handleClose,
22769
+ showBalance: showBalanceHeader,
22770
+ balanceAddress: recipientAddress,
22771
+ balanceChainType: destinationChainType === "ethereum" || destinationChainType === "solana" || destinationChainType === "bitcoin" || destinationChainType === "n1" ? destinationChainType : void 0,
22772
+ balanceChainId: destinationChainId,
22773
+ balanceTokenAddress: destinationTokenAddress,
22774
+ projectName: projectConfig?.project_name,
22775
+ publishableKey
22776
+ }
22777
+ ) }),
22778
+ renderMainMenuBody(),
22779
+ /* @__PURE__ */ jsx63("div", { className: "uf-flex-shrink-0", children: depositPoweredByFooter })
22780
+ ] }) : view === "transfer" ? /* @__PURE__ */ jsxs57(Fragment14, { children: [
22781
+ /* @__PURE__ */ jsx63(
22782
+ DepositHeader,
22783
+ {
22784
+ title: transferCryptoTitle,
22785
+ showBack: showBackTransfer,
22786
+ onBack: handleBack,
22787
+ onClose: handleClose,
22788
+ showBalance: showBalanceHeader,
22789
+ balanceAddress: recipientAddress,
22790
+ balanceChainType: destinationChainType === "ethereum" || destinationChainType === "solana" || destinationChainType === "bitcoin" || destinationChainType === "n1" ? destinationChainType : void 0,
22791
+ balanceChainId: destinationChainId,
22792
+ balanceTokenAddress: destinationTokenAddress,
22793
+ projectName: projectConfig?.project_name,
22794
+ publishableKey
22795
+ }
22796
+ ),
22797
+ /* @__PURE__ */ jsxs57("div", { className: "uf-flex uf-flex-col uf-gap-1.5", children: [
22798
+ standaloneNeedsDepositPrereq && depositPrerequisiteBody !== null ? /* @__PURE__ */ jsx63("div", { className: "uf-pb-4 uf-space-y-3", children: depositPrerequisiteBody }) : transferInputVariant === "single_input" ? /* @__PURE__ */ jsx63(
22799
+ TransferCryptoSingleInput,
22800
+ {
22801
+ userId,
22802
+ publishableKey,
22803
+ recipientAddress,
22804
+ destinationChainType,
22805
+ destinationChainId,
22806
+ destinationTokenAddress,
22807
+ defaultSourceChainType,
22808
+ defaultSourceChainId,
22809
+ defaultSourceTokenAddress,
22810
+ defaultSourceSymbol,
22811
+ depositConfirmationMode,
22812
+ onExecutionsChange: setDepositExecutions,
22813
+ onDepositSuccess: onDepositSuccessFor("transfer"),
22814
+ onDepositError: onDepositErrorFor("transfer"),
22815
+ wallets
22816
+ }
22817
+ ) : /* @__PURE__ */ jsx63(
22818
+ TransferCryptoDoubleInput,
22819
+ {
22820
+ userId,
22821
+ publishableKey,
22822
+ recipientAddress,
22823
+ destinationChainType,
22824
+ destinationChainId,
22825
+ destinationTokenAddress,
22826
+ defaultSourceChainType,
22827
+ defaultSourceChainId,
22828
+ defaultSourceTokenAddress,
22829
+ defaultSourceSymbol,
22830
+ depositConfirmationMode,
22831
+ onExecutionsChange: setDepositExecutions,
22832
+ onDepositSuccess: onDepositSuccessFor("transfer"),
22833
+ onDepositError: onDepositErrorFor("transfer"),
22834
+ wallets
22835
+ }
22836
+ ),
22837
+ depositPoweredByFooter
22838
+ ] })
22839
+ ] }) : view === "tracker" ? /* @__PURE__ */ jsxs57(Fragment14, { children: [
22840
+ /* @__PURE__ */ jsx63(
22841
+ DepositHeader,
22842
+ {
22843
+ title: selectedExecution ? "Deposit Details" : depositTrackerTitle,
22844
+ showBack: showBackTracker,
22845
+ onBack: handleBack,
22846
+ onClose: handleClose
22847
+ }
22848
+ ),
22849
+ /* @__PURE__ */ jsxs57("div", { className: "uf-flex uf-flex-col uf-gap-1.5", children: [
22850
+ /* @__PURE__ */ jsx63("div", { className: "uf-h-[460px] uf-overflow-y-auto [scrollbar-width:none] [&::-webkit-scrollbar]:uf-hidden", children: selectedExecution ? /* @__PURE__ */ jsx63(DepositDetailContent, { execution: selectedExecution }) : /* @__PURE__ */ jsx63("div", { className: "uf-space-y-2 uf-pb-8", children: allExecutions.length === 0 ? /* @__PURE__ */ jsx63("div", { className: "uf-py-8 uf-px-4 uf-text-center", children: /* @__PURE__ */ jsx63(
22851
+ "div",
22852
+ {
22853
+ className: "uf-text-sm",
22854
+ style: {
22855
+ color: components.container.subtitleColor,
22856
+ fontFamily: fonts.regular
22857
+ },
22858
+ children: "No deposits yet"
22859
+ }
22860
+ ) }) : allExecutions.map((execution) => /* @__PURE__ */ jsx63(
22861
+ DepositExecutionItem,
22862
+ {
22863
+ execution,
22864
+ onClick: () => setSelectedExecution(execution)
22865
+ },
22866
+ execution.id
22867
+ )) }) }),
22868
+ depositPoweredByFooter
22869
+ ] })
22870
+ ] }) : view === "card" ? /* @__PURE__ */ jsxs57(Fragment14, { children: [
22871
+ /* @__PURE__ */ jsx63(
22872
+ DepositHeader,
22873
+ {
22874
+ title: cardView === "quotes" ? t8.quotes : depositWithCardTitle,
22875
+ showBack: showBackCard,
22876
+ onBack: handleBack,
22877
+ onClose: handleClose,
22878
+ badge: cardView === "quotes" ? { count: quotesCount } : void 0,
22879
+ showBalance: showBalanceHeader,
22880
+ balanceAddress: recipientAddress,
22881
+ balanceChainType: destinationChainType === "ethereum" || destinationChainType === "solana" || destinationChainType === "bitcoin" || destinationChainType === "n1" ? destinationChainType : void 0,
22882
+ balanceChainId: destinationChainId,
22883
+ balanceTokenAddress: destinationTokenAddress,
22884
+ projectName: projectConfig?.project_name,
22885
+ publishableKey
22886
+ }
22887
+ ),
22888
+ /* @__PURE__ */ jsxs57("div", { className: "uf-flex uf-flex-col uf-gap-1.5", children: [
22889
+ standaloneNeedsDepositPrereq && depositPrerequisiteBody !== null ? /* @__PURE__ */ jsx63("div", { className: "uf-pb-4 uf-space-y-3", children: depositPrerequisiteBody }) : !showFiatOnramp ? (
22890
+ // Fiat on-ramp resolved hidden/disabled after a direct open
22891
+ // (e.g. platform `is_hidden` for a Stripe Link-only project).
22892
+ // Show a geo-restriction screen rather than the card UI so the
22893
+ // hard-hide is honoured without a flash of "Pay with Card".
22894
+ /* @__PURE__ */ jsx63(GeoRestrictionScreen, { methodName: "Card" })
22895
+ ) : /* @__PURE__ */ jsx63(
22896
+ BuyWithCard,
22897
+ {
22898
+ userId,
22899
+ publishableKey,
22900
+ view: cardView,
22901
+ onViewChange: handleCardViewChange,
22902
+ destinationTokenSymbol,
22903
+ recipientAddress,
22904
+ destinationChainType,
22905
+ destinationChainId,
22906
+ destinationTokenAddress,
22907
+ onDepositSuccess: onDepositSuccessFor("card"),
22908
+ onDepositError: onDepositErrorFor("card"),
22909
+ onEvent,
22910
+ themeClass,
22911
+ wallets,
22912
+ assetCdnUrl: projectConfig?.asset_cdn_url,
22913
+ hideDepositFlowInfo,
22914
+ hideDisplayDescription
22388
22915
  }
22916
+ ),
22917
+ depositPoweredByFooter
22918
+ ] })
22919
+ ] }) : view === "exchange" ? /* @__PURE__ */ jsxs57(Fragment14, { children: [
22920
+ /* @__PURE__ */ jsx63(
22921
+ DepositHeader,
22922
+ {
22923
+ title: payWithExchangeTitle,
22924
+ showBack: exchangeView === "pending" || sessionOpenedFromMenu,
22925
+ onBack: handleBack,
22926
+ onClose: handleClose
22389
22927
  }
22390
- }
22391
- ),
22392
- depositPoweredByFooter
22393
- ] })
22394
- ] }) : null })
22928
+ ),
22929
+ /* @__PURE__ */ jsxs57("div", { className: "uf-flex uf-flex-col uf-gap-1.5", children: [
22930
+ /* @__PURE__ */ jsx63(
22931
+ PayWithExchange,
22932
+ {
22933
+ userId,
22934
+ publishableKey,
22935
+ exchanges,
22936
+ view: exchangeView,
22937
+ onViewChange: setExchangeView,
22938
+ destinationTokenSymbol,
22939
+ recipientAddress,
22940
+ destinationChainType,
22941
+ destinationChainId,
22942
+ destinationTokenAddress,
22943
+ onDepositSuccess: onDepositSuccessFor("pay_with_exchange"),
22944
+ onDepositError: onDepositErrorFor("pay_with_exchange"),
22945
+ wallets,
22946
+ defaultToken: defaultToken ?? null
22947
+ }
22948
+ ),
22949
+ depositPoweredByFooter
22950
+ ] })
22951
+ ] }) : view === "coinbase_connect" ? /* @__PURE__ */ jsxs57("div", { className: "uf-flex uf-flex-col uf-gap-1.5", children: [
22952
+ /* @__PURE__ */ jsx63(
22953
+ CoinbaseConnect,
22954
+ {
22955
+ publishableKey,
22956
+ userId,
22957
+ wallets,
22958
+ recipientAddress,
22959
+ destinationTokenAddress: destinationTokenAddress ?? "",
22960
+ destinationChainId: destinationChainId ?? "",
22961
+ destinationChainType: destinationChainType ?? "",
22962
+ onDepositSuccess: onDepositSuccessFor("exchange_connect"),
22963
+ onDepositError: onDepositErrorFor("exchange_connect"),
22964
+ onTransferError: (error) => {
22965
+ onDepositErrorFor("exchange_connect")?.({
22966
+ message: error.message,
22967
+ error
22968
+ });
22969
+ },
22970
+ onBack: handleBack,
22971
+ onClose: handleClose,
22972
+ onDisconnect: handleExchangeDisconnect,
22973
+ skipToHoldings: coinbaseSkipToHoldings,
22974
+ canGoBack: sessionOpenedFromMenu,
22975
+ onExecutionsChange: setDepositExecutions,
22976
+ defaultSourceChainType,
22977
+ defaultSourceChainId,
22978
+ defaultSourceTokenAddress,
22979
+ defaultSourceSymbol
22980
+ }
22981
+ ),
22982
+ depositPoweredByFooter
22983
+ ] }) : view === "wallet_connect" ? (
22984
+ // Mobile: flex-fill the full-height dialog body so the wallet list
22985
+ // can grow and scroll, with the footer pinned to the bottom.
22986
+ // Desktop (sm:): stays content-sized.
22987
+ /* @__PURE__ */ jsxs57(
22988
+ "div",
22989
+ {
22990
+ className: "uf-flex uf-flex-col uf-gap-1.5 uf-min-h-0 uf-flex-1 sm:uf-flex-none",
22991
+ children: [
22992
+ /* @__PURE__ */ jsx63(
22993
+ WalletConnect,
22994
+ {
22995
+ walletInfo: browserWalletInfo ?? void 0,
22996
+ depositWallet: browserWalletInfo?.depositWallet ?? void 0,
22997
+ wallets,
22998
+ userId,
22999
+ publishableKey,
23000
+ assetCdnUrl: projectConfig?.asset_cdn_url,
23001
+ projectName: projectConfig?.project_name,
23002
+ onError: (error) => {
23003
+ onDepositErrorFor("wallet_connect")?.({
23004
+ message: error.message,
23005
+ error
23006
+ });
23007
+ },
23008
+ onDepositSuccess: onDepositSuccessFor("wallet_connect"),
23009
+ onDepositError: onDepositErrorFor("wallet_connect"),
23010
+ amountQuickSelect: browserWalletAmountQuickSelect,
23011
+ onWalletDisconnect: handleWalletDisconnect,
23012
+ onWalletConnected: (info, dw) => {
23013
+ setBrowserWalletInfo({ ...info, depositWallet: dw });
23014
+ setStoredWalletState(info.type);
23015
+ setBrowserWalletChainType(dw.chain_type === "solana" ? "solana" : "ethereum");
23016
+ },
23017
+ onBack: handleBack,
23018
+ onClose: handleClose,
23019
+ defaultSourceChainType,
23020
+ defaultSourceChainId,
23021
+ defaultSourceTokenAddress,
23022
+ defaultSourceSymbol,
23023
+ canGoBack: sessionOpenedFromMenu,
23024
+ depositWalletsLoading: walletsLoading
23025
+ }
23026
+ ),
23027
+ depositPoweredByFooter
23028
+ ]
23029
+ }
23030
+ )
23031
+ ) : view === "bank_transfer" ? /* @__PURE__ */ jsxs57(Fragment14, { children: [
23032
+ /* @__PURE__ */ jsx63(
23033
+ DepositHeader,
23034
+ {
23035
+ title: t8.bankTransfer.title,
23036
+ showBack: bankTransferView !== "providers" || sessionOpenedFromMenu,
23037
+ onBack: handleBack,
23038
+ onClose: handleClose
23039
+ }
23040
+ ),
23041
+ /* @__PURE__ */ jsxs57("div", { className: "uf-flex uf-flex-col uf-gap-1.5", children: [
23042
+ bankTransferProvidersLoading ? /* @__PURE__ */ jsx63(SkeletonButton, { variant: "with-icons" }) : !hasEnabledBankTransferProvider ? /* @__PURE__ */ jsx63(GeoRestrictionScreen, { methodName: "Bank Transfer" }) : /* @__PURE__ */ jsx63(
23043
+ BankTransfer,
23044
+ {
23045
+ userId,
23046
+ publishableKey,
23047
+ view: bankTransferView,
23048
+ onViewChange: setBankTransferView,
23049
+ recipientAddress,
23050
+ destinationChainType,
23051
+ destinationChainId,
23052
+ destinationTokenAddress,
23053
+ destinationTokenSymbol,
23054
+ wallets,
23055
+ defaultToken: defaultToken ?? null,
23056
+ assetCdnUrl: projectConfig?.asset_cdn_url,
23057
+ onEvent,
23058
+ onDepositSuccess,
23059
+ onDepositError
23060
+ }
23061
+ ),
23062
+ depositPoweredByFooter
23063
+ ] })
23064
+ ] }) : view === "stripe_link" ? /* @__PURE__ */ jsxs57(Fragment14, { children: [
23065
+ /* @__PURE__ */ jsx63(
23066
+ DepositHeader,
23067
+ {
23068
+ title: "Deposit with Link",
23069
+ showBack: stripeLinkStep !== "checkout" && stripeLinkStep !== "success",
23070
+ onBack: handleBack,
23071
+ showClose: stripeLinkStep !== "checkout",
23072
+ onClose: handleClose
23073
+ }
23074
+ ),
23075
+ /* @__PURE__ */ jsxs57("div", { className: "uf-flex uf-flex-col uf-gap-1.5", children: [
23076
+ isLoadingIp ? (
23077
+ // Hold the geo decision until IP resolves so we don't mount
23078
+ // PayWithStripeLink (which kicks off config/OAuth work) for a
23079
+ // deep-link user who turns out to be outside the US.
23080
+ /* @__PURE__ */ jsx63(SkeletonButton, { variant: "with-icons" })
23081
+ ) : !showStripeLink ? (
23082
+ // Stripe Link's crypto on-ramp is US-only. On a direct open
23083
+ // (initialScreen="stripe_link") the row isn't in a menu to
23084
+ // fall back to, so show a geo-restriction screen rather than
23085
+ // the Link UI.
23086
+ /* @__PURE__ */ jsx63(
23087
+ GeoRestrictionScreen,
23088
+ {
23089
+ methodName: t8.stripeLink.title,
23090
+ message: "Pay with Link is only available in the US."
23091
+ }
23092
+ )
23093
+ ) : /* @__PURE__ */ jsx63(
23094
+ PayWithStripeLink,
23095
+ {
23096
+ userId,
23097
+ publishableKey,
23098
+ recipientAddress,
23099
+ destinationChainType,
23100
+ destinationChainId,
23101
+ destinationTokenAddress,
23102
+ wallets,
23103
+ email: userEmail,
23104
+ iconUrl: projectConfig?.asset_cdn_url ? `${projectConfig.asset_cdn_url}/api/public/icons/onramps/svg/link.svg` : void 0,
23105
+ step: stripeLinkStep,
23106
+ onStepChange: setStripeLinkStep,
23107
+ backHandlerRef: stripeLinkBackRef,
23108
+ onDepositSuccess,
23109
+ onDepositError
23110
+ }
23111
+ ),
23112
+ depositPoweredByFooter
23113
+ ] })
23114
+ ] }) : view === "cashapp" ? /* @__PURE__ */ jsxs57(Fragment14, { children: [
23115
+ /* @__PURE__ */ jsx63(
23116
+ DepositHeader,
23117
+ {
23118
+ title: cashAppView !== "amount" && cashAppAmount ? `Pay $${cashAppAmount} via Cash App` : "Pay with Cash App",
23119
+ showBack: cashAppView !== "amount" || sessionOpenedFromMenu,
23120
+ onBack: handleBack,
23121
+ onClose: handleClose
23122
+ }
23123
+ ),
23124
+ /* @__PURE__ */ jsxs57("div", { className: "uf-flex uf-flex-col uf-gap-1.5", children: [
23125
+ /* @__PURE__ */ jsx63(
23126
+ PayWithCashApp,
23127
+ {
23128
+ userId,
23129
+ publishableKey,
23130
+ recipientAddress,
23131
+ destinationChainType,
23132
+ destinationChainId,
23133
+ destinationTokenAddress,
23134
+ cashAppIconUrl: projectConfig?.asset_cdn_url ? `${projectConfig.asset_cdn_url}/api/public/icons/onramps/svg/cashapp.svg` : void 0,
23135
+ view: cashAppView,
23136
+ onViewChange: setCashAppView,
23137
+ onAmountChange: setCashAppAmount,
23138
+ onEvent,
23139
+ onDepositSuccess: onDepositSuccessFor("cashapp"),
23140
+ onDepositError: onDepositErrorFor("cashapp"),
23141
+ wallets
23142
+ }
23143
+ ),
23144
+ depositPoweredByFooter
23145
+ ] })
23146
+ ] }) : view === "apple_pay" ? /* @__PURE__ */ jsxs57(Fragment14, { children: [
23147
+ /* @__PURE__ */ jsx63(
23148
+ DepositHeader,
23149
+ {
23150
+ title: applePayHeaderTitle,
23151
+ showBack: applePayShowBack,
23152
+ onBack: () => {
23153
+ const handled = applePayHandleRef.current?.requestBack() ?? false;
23154
+ if (!handled) handleBack();
23155
+ },
23156
+ onClose: handleClose
23157
+ }
23158
+ ),
23159
+ /* @__PURE__ */ jsxs57("div", { className: "uf-flex uf-flex-col uf-gap-1.5", children: [
23160
+ applePayProvidersLoading ? /* @__PURE__ */ jsx63(SkeletonButton, { variant: "with-icons" }) : !hasEnabledApplePayProvider ? /* @__PURE__ */ jsx63(GeoRestrictionScreen, { methodName: "Apple Pay" }) : /* @__PURE__ */ jsx63(
23161
+ BuyWithApplePay,
23162
+ {
23163
+ ref: applePayHandleRef,
23164
+ userId,
23165
+ publishableKey,
23166
+ destinationChainType,
23167
+ destinationChainId,
23168
+ destinationTokenAddress,
23169
+ userEmail,
23170
+ wallets,
23171
+ onViewChange: setApplePayView,
23172
+ onEvent,
23173
+ onDepositSuccess: onDepositSuccessFor("apple_pay"),
23174
+ onDepositError: onDepositErrorFor("apple_pay"),
23175
+ exitLabel: sessionOpenedFromMenu ? "Return" : "Close",
23176
+ onExit: () => {
23177
+ if (sessionOpenedFromMenu) {
23178
+ setView("main");
23179
+ } else {
23180
+ handleClose();
23181
+ }
23182
+ }
23183
+ }
23184
+ ),
23185
+ depositPoweredByFooter
23186
+ ] })
23187
+ ] }) : null
23188
+ }
23189
+ )
22395
23190
  ]
22396
23191
  }
22397
23192
  )
@@ -22400,7 +23195,7 @@ function DepositModal({
22400
23195
  }
22401
23196
 
22402
23197
  // src/components/checkout/CheckoutModal.tsx
22403
- import { useState as useState41, useEffect as useEffect35, useLayoutEffect as useLayoutEffect3, useCallback as useCallback9, useRef as useRef13, useMemo as useMemo13 } from "react";
23198
+ import { useState as useState41, useEffect as useEffect35, useLayoutEffect as useLayoutEffect3, useCallback as useCallback9, useRef as useRef13, useMemo as useMemo14 } from "react";
22404
23199
  import { AlertTriangle as AlertTriangle4, ChevronRight as ChevronRight19 } from "lucide-react";
22405
23200
 
22406
23201
  // src/hooks/use-payment-intent.ts
@@ -22585,11 +23380,11 @@ function CheckoutModal({
22585
23380
  );
22586
23381
  }
22587
23382
  }, [emitCheckoutSuccess, paymentIntent, view]);
22588
- const wallets = useMemo13(() => {
23383
+ const wallets = useMemo14(() => {
22589
23384
  if (!paymentIntent) return [];
22590
23385
  return mapDepositAddressesToWallets(paymentIntent.deposit_addresses, paymentIntent);
22591
23386
  }, [paymentIntent]);
22592
- const formatCryptoAmount = useMemo13(() => {
23387
+ const formatCryptoAmount = useMemo14(() => {
22593
23388
  if (!paymentIntent) return (_) => "";
22594
23389
  const decimals = paymentIntent.destination_token_decimals ?? 6;
22595
23390
  const symbol = paymentIntent.currency.toUpperCase();
@@ -22599,7 +23394,7 @@ function CheckoutModal({
22599
23394
  return `${formatted} ${symbol}`;
22600
23395
  };
22601
23396
  }, [paymentIntent]);
22602
- const remainingAmountUsd = useMemo13(() => {
23397
+ const remainingAmountUsd = useMemo14(() => {
22603
23398
  if (!paymentIntent) return void 0;
22604
23399
  const total = parseFloat(paymentIntent.destination_amount_usd || paymentIntent.amount_usd);
22605
23400
  const received = parseFloat(
@@ -22611,7 +23406,7 @@ function CheckoutModal({
22611
23406
  return remaining > 0 ? remaining.toFixed(2) : "0.00";
22612
23407
  }, [paymentIntent]);
22613
23408
  const [selectedSource, setSelectedSource] = useState41(null);
22614
- const remainingDestinationAmount = useMemo13(() => {
23409
+ const remainingDestinationAmount = useMemo14(() => {
22615
23410
  if (!paymentIntent) return "0";
22616
23411
  const remaining = BigInt(paymentIntent.destination_amount) - BigInt(paymentIntent.destination_amount_received);
22617
23412
  return remaining > 0n ? remaining.toString() : "0";
@@ -22633,7 +23428,7 @@ function CheckoutModal({
22633
23428
  stablecoinParity: paymentIntent?.stablecoin_parity ?? false,
22634
23429
  enabled: open && view === "transfer" && !!paymentIntent && !!selectedSource && remainingDestinationAmount !== "0"
22635
23430
  });
22636
- const effectiveCheckoutQuote = useMemo13(() => {
23431
+ const effectiveCheckoutQuote = useMemo14(() => {
22637
23432
  if (!sourceQuote || !selectedSource) return null;
22638
23433
  const baseQuote = {
22639
23434
  sourceAmount: sourceQuote.source_amount,
@@ -22845,253 +23640,275 @@ function CheckoutModal({
22845
23640
  return /* @__PURE__ */ jsx64(PortalContainerProvider, { value: null, children: /* @__PURE__ */ jsx64(Dialog, { open, onOpenChange: handleClose, modal: true, children: /* @__PURE__ */ jsx64(
22846
23641
  DialogContent,
22847
23642
  {
22848
- className: `sm:uf-max-w-[400px] uf-border-secondary uf-text-foreground uf-gap-0 [&>button]:uf-hidden uf-p-0 uf-overflow-visible ${view === "main" ? "!uf-top-auto !uf-h-auto !uf-max-h-[60vh] sm:!uf-max-h-none sm:!uf-top-[50%]" : "!uf-top-0 !uf-h-full sm:!uf-h-auto sm:!uf-top-[50%]"} ${themeClass}`,
23643
+ className: `sm:uf-max-w-[400px] uf-border-secondary uf-text-foreground uf-gap-0 [&>button]:uf-hidden uf-p-0 uf-overflow-visible ${view === "main" ? "!uf-top-auto !uf-h-auto !uf-max-h-[60vh] sm:!uf-max-h-none sm:!uf-top-[50%]" : "!uf-top-0 !uf-h-full sm:!uf-h-auto sm:!uf-top-[50%]"} ${// wallet_connect fills the full-height mobile sheet. DialogContent is a
23644
+ // grid whose single auto row only grows to fill free space and never
23645
+ // shrinks below content, so a long wallet list would balloon the row
23646
+ // past the viewport and push the footer off-screen. Clamp the row to
23647
+ // the modal height with minmax(0,1fr) so the inner overflow-y-auto list
23648
+ // scrolls with the footer pinned. Reset to content-sized on desktop.
23649
+ view === "wallet_connect" ? "[grid-template-rows:minmax(0,1fr)] sm:[grid-template-rows:none]" : ""} ${themeClass}`,
22849
23650
  style: { backgroundColor: colors2.background },
22850
23651
  onPointerDownOutside: (e) => e.preventDefault(),
22851
23652
  onInteractOutside: (e) => e.preventDefault(),
22852
- children: /* @__PURE__ */ jsx64(ThemeStyleInjector, { children: view === "main" ? /* @__PURE__ */ jsxs58(Fragment15, { children: [
22853
- /* @__PURE__ */ jsx64(DepositHeader, { title: modalTitle || "Checkout", showClose: true, onClose: handleClose }),
22854
- /* @__PURE__ */ jsxs58("div", { className: "uf-flex uf-flex-col uf-gap-1.5", children: [
22855
- piLoading ? /* @__PURE__ */ jsxs58("div", { className: "uf-space-y-3", children: [
22856
- /* @__PURE__ */ jsx64(
22857
- "div",
22858
- {
22859
- className: "uf-rounded-xl uf-p-4 uf-animate-pulse",
22860
- style: {
22861
- backgroundColor: components.card.backgroundColor,
22862
- borderRadius: components.card.borderRadius,
22863
- border: `${components.card.borderWidth}px solid ${components.card.borderColor}`
22864
- },
22865
- children: /* @__PURE__ */ jsxs58("div", { className: "uf-flex uf-flex-col uf-items-center uf-gap-2", children: [
22866
- /* @__PURE__ */ jsx64(
22867
- "div",
23653
+ children: /* @__PURE__ */ jsx64(
23654
+ ThemeStyleInjector,
23655
+ {
23656
+ className: view === "wallet_connect" ? "uf-flex uf-min-h-0 uf-flex-col" : void 0,
23657
+ children: view === "main" ? /* @__PURE__ */ jsxs58(Fragment15, { children: [
23658
+ /* @__PURE__ */ jsx64(DepositHeader, { title: modalTitle || "Checkout", showClose: true, onClose: handleClose }),
23659
+ /* @__PURE__ */ jsxs58("div", { className: "uf-flex uf-flex-col uf-gap-1.5", children: [
23660
+ piLoading ? /* @__PURE__ */ jsxs58("div", { className: "uf-space-y-3", children: [
23661
+ /* @__PURE__ */ jsx64(
23662
+ "div",
23663
+ {
23664
+ className: "uf-rounded-xl uf-p-4 uf-animate-pulse",
23665
+ style: {
23666
+ backgroundColor: components.card.backgroundColor,
23667
+ borderRadius: components.card.borderRadius,
23668
+ border: `${components.card.borderWidth}px solid ${components.card.borderColor}`
23669
+ },
23670
+ children: /* @__PURE__ */ jsxs58("div", { className: "uf-flex uf-flex-col uf-items-center uf-gap-2", children: [
23671
+ /* @__PURE__ */ jsx64(
23672
+ "div",
23673
+ {
23674
+ className: "uf-h-8 uf-w-24 uf-rounded",
23675
+ style: {
23676
+ backgroundColor: components.card.borderColor
23677
+ }
23678
+ }
23679
+ ),
23680
+ /* @__PURE__ */ jsx64(
23681
+ "div",
23682
+ {
23683
+ className: "uf-h-4 uf-w-16 uf-rounded",
23684
+ style: {
23685
+ backgroundColor: components.card.borderColor
23686
+ }
23687
+ }
23688
+ )
23689
+ ] })
23690
+ }
23691
+ ),
23692
+ /* @__PURE__ */ jsx64(SkeletonButton2, {}),
23693
+ /* @__PURE__ */ jsx64(SkeletonButton2, {})
23694
+ ] }) : piError ? /* @__PURE__ */ jsxs58("div", { className: "uf-flex uf-flex-col uf-items-center uf-justify-center uf-py-8 uf-px-4 uf-text-center", children: [
23695
+ /* @__PURE__ */ jsx64("div", { className: "uf-w-16 uf-h-16 uf-rounded-full uf-bg-muted uf-flex uf-items-center uf-justify-center uf-mb-4", children: /* @__PURE__ */ jsx64(AlertTriangle4, { className: "uf-w-8 uf-h-8 uf-text-muted-foreground" }) }),
23696
+ /* @__PURE__ */ jsx64(
23697
+ "h3",
23698
+ {
23699
+ className: "uf-text-lg uf-font-semibold uf-mb-2",
23700
+ style: {
23701
+ color: colors2.foreground,
23702
+ fontFamily: fonts.semibold
23703
+ },
23704
+ children: "Unable to Load Checkout"
23705
+ }
23706
+ ),
23707
+ /* @__PURE__ */ jsx64(
23708
+ "p",
23709
+ {
23710
+ className: "uf-text-sm uf-max-w-[280px]",
23711
+ style: {
23712
+ color: colors2.foregroundMuted,
23713
+ fontFamily: fonts.regular
23714
+ },
23715
+ children: piError instanceof Error ? piError.message : "Something went wrong. Please try again."
23716
+ }
23717
+ )
23718
+ ] }) : paymentIntent ? /* @__PURE__ */ jsxs58("div", { className: "uf-space-y-3", children: [
23719
+ progressSection,
23720
+ (paymentIntent.status === "requires_payment" || paymentIntent.status === "processing") && /* @__PURE__ */ jsxs58(Fragment15, { children: [
23721
+ showTransferCrypto && /* @__PURE__ */ jsx64(
23722
+ TransferCryptoButton,
22868
23723
  {
22869
- className: "uf-h-8 uf-w-24 uf-rounded",
22870
- style: {
22871
- backgroundColor: components.card.borderColor
22872
- }
23724
+ onClick: () => {
23725
+ lastCheckoutMethodRef.current = "transfer";
23726
+ setView("transfer");
23727
+ },
23728
+ title: i18n.checkoutModal.transferCrypto.title,
23729
+ subtitle: i18n.checkoutModal.transferCrypto.subtitle,
23730
+ featuredTokens: projectConfig?.transfer_crypto.networks
22873
23731
  }
22874
23732
  ),
22875
- /* @__PURE__ */ jsx64(
22876
- "div",
23733
+ showConnectWallet && /* @__PURE__ */ jsx64(
23734
+ BrowserWalletButton,
22877
23735
  {
22878
- className: "uf-h-4 uf-w-16 uf-rounded",
22879
- style: {
22880
- backgroundColor: components.card.borderColor
22881
- }
23736
+ onClick: handleBrowserWalletClick,
23737
+ onConnectClick: handleWalletConnectClick,
23738
+ onDisconnect: handleWalletDisconnect,
23739
+ chainType: browserWalletChainType,
23740
+ publishableKey,
23741
+ featuredWallets: projectConfig?.connect_wallet?.wallets,
23742
+ subtitle: i18n.checkoutModal.browserWallet.subtitle
22882
23743
  }
22883
23744
  )
22884
23745
  ] })
22885
- }
22886
- ),
22887
- /* @__PURE__ */ jsx64(SkeletonButton2, {}),
22888
- /* @__PURE__ */ jsx64(SkeletonButton2, {})
22889
- ] }) : piError ? /* @__PURE__ */ jsxs58("div", { className: "uf-flex uf-flex-col uf-items-center uf-justify-center uf-py-8 uf-px-4 uf-text-center", children: [
22890
- /* @__PURE__ */ jsx64("div", { className: "uf-w-16 uf-h-16 uf-rounded-full uf-bg-muted uf-flex uf-items-center uf-justify-center uf-mb-4", children: /* @__PURE__ */ jsx64(AlertTriangle4, { className: "uf-w-8 uf-h-8 uf-text-muted-foreground" }) }),
23746
+ ] }) : null,
23747
+ poweredByFooter
23748
+ ] })
23749
+ ] }) : view === "transfer" ? /* @__PURE__ */ jsxs58(Fragment15, { children: [
22891
23750
  /* @__PURE__ */ jsx64(
22892
- "h3",
23751
+ DepositHeader,
22893
23752
  {
22894
- className: "uf-text-lg uf-font-semibold uf-mb-2",
22895
- style: {
22896
- color: colors2.foreground,
22897
- fontFamily: fonts.semibold
22898
- },
22899
- children: "Unable to Load Checkout"
23753
+ title: modalTitle || "Checkout",
23754
+ showBack: true,
23755
+ onBack: handleBack,
23756
+ onClose: handleClose
22900
23757
  }
22901
23758
  ),
22902
- /* @__PURE__ */ jsx64(
22903
- "p",
22904
- {
22905
- className: "uf-text-sm uf-max-w-[280px]",
22906
- style: {
22907
- color: colors2.foregroundMuted,
22908
- fontFamily: fonts.regular
22909
- },
22910
- children: piError instanceof Error ? piError.message : "Something went wrong. Please try again."
22911
- }
22912
- )
22913
- ] }) : paymentIntent ? /* @__PURE__ */ jsxs58("div", { className: "uf-space-y-3", children: [
22914
- progressSection,
22915
- (paymentIntent.status === "requires_payment" || paymentIntent.status === "processing") && /* @__PURE__ */ jsxs58(Fragment15, { children: [
22916
- showTransferCrypto && /* @__PURE__ */ jsx64(
22917
- TransferCryptoButton,
22918
- {
22919
- onClick: () => {
22920
- lastCheckoutMethodRef.current = "transfer";
22921
- setView("transfer");
22922
- },
22923
- title: i18n.checkoutModal.transferCrypto.title,
22924
- subtitle: i18n.checkoutModal.transferCrypto.subtitle,
22925
- featuredTokens: projectConfig?.transfer_crypto.networks
22926
- }
22927
- ),
22928
- showConnectWallet && /* @__PURE__ */ jsx64(
22929
- BrowserWalletButton,
22930
- {
22931
- onClick: handleBrowserWalletClick,
22932
- onConnectClick: handleWalletConnectClick,
22933
- onDisconnect: handleWalletDisconnect,
22934
- chainType: browserWalletChainType,
22935
- publishableKey,
22936
- featuredWallets: projectConfig?.connect_wallet?.wallets,
22937
- subtitle: i18n.checkoutModal.browserWallet.subtitle
22938
- }
22939
- )
22940
- ] })
22941
- ] }) : null,
22942
- poweredByFooter
22943
- ] })
22944
- ] }) : view === "transfer" ? /* @__PURE__ */ jsxs58(Fragment15, { children: [
22945
- /* @__PURE__ */ jsx64(
22946
- DepositHeader,
22947
- {
22948
- title: modalTitle || "Checkout",
22949
- showBack: true,
22950
- onBack: handleBack,
22951
- onClose: handleClose
22952
- }
22953
- ),
22954
- /* @__PURE__ */ jsxs58("div", { className: "uf-flex uf-flex-col uf-gap-1.5", children: [
22955
- paymentIntent ? /* @__PURE__ */ jsxs58(Fragment15, { children: [
22956
- (() => {
22957
- const receivedUsd = parseFloat(
22958
- paymentIntent.destination_amount_received_usd || paymentIntent.amount_received_usd
22959
- );
22960
- const totalUsd = parseFloat(
22961
- paymentIntent.destination_amount_usd || paymentIntent.amount_usd
22962
- );
22963
- const pct = totalUsd > 0 ? Math.min(receivedUsd / totalUsd * 100, 100) : 0;
22964
- return /* @__PURE__ */ jsxs58("div", { className: "uf-space-y-2", children: [
22965
- /* @__PURE__ */ jsxs58("div", { className: "uf-flex uf-items-center uf-justify-between", children: [
22966
- /* @__PURE__ */ jsx64(
22967
- "span",
22968
- {
22969
- className: "uf-text-xs",
22970
- style: { color: colors2.foregroundMuted, fontFamily: fonts.regular },
22971
- children: "Received"
22972
- }
22973
- ),
22974
- /* @__PURE__ */ jsxs58(
22975
- "span",
22976
- {
22977
- className: "uf-text-xs",
22978
- style: { color: colors2.foreground, fontFamily: fonts.medium },
22979
- children: [
22980
- "$",
22981
- receivedUsd.toFixed(2),
22982
- " / $",
22983
- totalUsd.toFixed(2)
22984
- ]
22985
- }
22986
- )
22987
- ] }),
22988
- /* @__PURE__ */ jsx64(
22989
- "div",
22990
- {
22991
- className: "uf-w-full uf-h-1.5 uf-rounded-full uf-overflow-hidden",
22992
- style: { backgroundColor: colors2.border },
22993
- children: /* @__PURE__ */ jsx64(
23759
+ /* @__PURE__ */ jsxs58("div", { className: "uf-flex uf-flex-col uf-gap-1.5", children: [
23760
+ paymentIntent ? /* @__PURE__ */ jsxs58(Fragment15, { children: [
23761
+ (() => {
23762
+ const receivedUsd = parseFloat(
23763
+ paymentIntent.destination_amount_received_usd || paymentIntent.amount_received_usd
23764
+ );
23765
+ const totalUsd = parseFloat(
23766
+ paymentIntent.destination_amount_usd || paymentIntent.amount_usd
23767
+ );
23768
+ const pct = totalUsd > 0 ? Math.min(receivedUsd / totalUsd * 100, 100) : 0;
23769
+ return /* @__PURE__ */ jsxs58("div", { className: "uf-space-y-2", children: [
23770
+ /* @__PURE__ */ jsxs58("div", { className: "uf-flex uf-items-center uf-justify-between", children: [
23771
+ /* @__PURE__ */ jsx64(
23772
+ "span",
23773
+ {
23774
+ className: "uf-text-xs",
23775
+ style: { color: colors2.foregroundMuted, fontFamily: fonts.regular },
23776
+ children: "Received"
23777
+ }
23778
+ ),
23779
+ /* @__PURE__ */ jsxs58(
23780
+ "span",
23781
+ {
23782
+ className: "uf-text-xs",
23783
+ style: { color: colors2.foreground, fontFamily: fonts.medium },
23784
+ children: [
23785
+ "$",
23786
+ receivedUsd.toFixed(2),
23787
+ " / $",
23788
+ totalUsd.toFixed(2)
23789
+ ]
23790
+ }
23791
+ )
23792
+ ] }),
23793
+ /* @__PURE__ */ jsx64(
22994
23794
  "div",
22995
23795
  {
22996
- className: "uf-h-full uf-rounded-full uf-transition-all uf-duration-500",
22997
- style: {
22998
- width: `${pct}%`,
22999
- backgroundColor: paymentIntent.status === "succeeded" ? "rgb(34, 197, 94)" : colors2.primary
23000
- }
23796
+ className: "uf-w-full uf-h-1.5 uf-rounded-full uf-overflow-hidden",
23797
+ style: { backgroundColor: colors2.border },
23798
+ children: /* @__PURE__ */ jsx64(
23799
+ "div",
23800
+ {
23801
+ className: "uf-h-full uf-rounded-full uf-transition-all uf-duration-500",
23802
+ style: {
23803
+ width: `${pct}%`,
23804
+ backgroundColor: paymentIntent.status === "succeeded" ? "rgb(34, 197, 94)" : colors2.primary
23805
+ }
23806
+ }
23807
+ )
23001
23808
  }
23002
23809
  )
23810
+ ] });
23811
+ })(),
23812
+ /* @__PURE__ */ jsx64(
23813
+ TransferCryptoSingleInput,
23814
+ {
23815
+ userId: paymentIntent.user_id || "",
23816
+ publishableKey,
23817
+ clientSecret,
23818
+ recipientAddress: paymentIntent.recipient_address,
23819
+ destinationChainType: paymentIntent.destination_chain_type,
23820
+ destinationChainId: paymentIntent.destination_chain_id,
23821
+ destinationTokenAddress: paymentIntent.destination_token_address,
23822
+ defaultSourceChainType,
23823
+ defaultSourceChainId,
23824
+ defaultSourceTokenAddress,
23825
+ defaultSourceSymbol,
23826
+ depositConfirmationMode: "auto_ui",
23827
+ wallets,
23828
+ onSourceTokenChange: setSelectedSource,
23829
+ persistCheckingIndicator: true,
23830
+ productType: "payment",
23831
+ checkoutQuote: effectiveCheckoutQuote,
23832
+ isCheckoutQuoteLoading: isQuoteLoading || isQuoteFetching
23003
23833
  }
23004
23834
  )
23005
- ] });
23006
- })(),
23007
- /* @__PURE__ */ jsx64(
23008
- TransferCryptoSingleInput,
23835
+ ] }) : /* @__PURE__ */ jsx64(SkeletonButton2, {}),
23836
+ poweredByFooter
23837
+ ] })
23838
+ ] }) : view === "wallet_connect" && paymentIntent ? (
23839
+ // Mobile: flex-fill the full-height sheet so the wallet list grows and
23840
+ // scrolls with the footer pinned. Desktop (sm:): content-sized.
23841
+ /* @__PURE__ */ jsxs58(
23842
+ "div",
23009
23843
  {
23010
- userId: paymentIntent.user_id || "",
23011
- publishableKey,
23012
- clientSecret,
23013
- recipientAddress: paymentIntent.recipient_address,
23014
- destinationChainType: paymentIntent.destination_chain_type,
23015
- destinationChainId: paymentIntent.destination_chain_id,
23016
- destinationTokenAddress: paymentIntent.destination_token_address,
23017
- defaultSourceChainType,
23018
- defaultSourceChainId,
23019
- defaultSourceTokenAddress,
23020
- defaultSourceSymbol,
23021
- depositConfirmationMode: "auto_ui",
23022
- wallets,
23023
- onSourceTokenChange: setSelectedSource,
23024
- persistCheckingIndicator: true,
23025
- productType: "payment",
23026
- checkoutQuote: effectiveCheckoutQuote,
23027
- isCheckoutQuoteLoading: isQuoteLoading || isQuoteFetching
23844
+ className: "uf-flex uf-flex-col uf-gap-1.5 uf-min-h-0 uf-flex-1 sm:uf-flex-none",
23845
+ children: [
23846
+ /* @__PURE__ */ jsx64(
23847
+ WalletConnect,
23848
+ {
23849
+ walletInfo: browserWalletInfo ?? void 0,
23850
+ depositWallet: browserWalletInfo?.depositWallet ?? void 0,
23851
+ wallets,
23852
+ userId: paymentIntent.user_id || "",
23853
+ publishableKey,
23854
+ clientSecret,
23855
+ prefillAmountUsd: remainingAmountUsd,
23856
+ checkoutAmountUsd: paymentIntent.amount_usd,
23857
+ checkoutReceivedUsd: paymentIntent.amount_received_usd,
23858
+ checkoutDestination: {
23859
+ chainType: paymentIntent.destination_chain_type,
23860
+ chainId: paymentIntent.destination_chain_id,
23861
+ tokenAddress: paymentIntent.destination_token_address,
23862
+ decimals: paymentIntent.destination_token_decimals ?? 6
23863
+ },
23864
+ productType: "payment",
23865
+ stablecoinParity: paymentIntent.stablecoin_parity ?? false,
23866
+ checkoutRemainingBaseUnits: (() => {
23867
+ const remaining = BigInt(paymentIntent.amount) - BigInt(paymentIntent.amount_received);
23868
+ return remaining > 0n ? remaining.toString() : "0";
23869
+ })(),
23870
+ onSuccess: (_txHash) => {
23871
+ emitCheckoutSuccess(
23872
+ {
23873
+ paymentIntentId: paymentIntent.id,
23874
+ status: "processing",
23875
+ paymentIntent
23876
+ },
23877
+ "wallet_connect"
23878
+ );
23879
+ },
23880
+ onError: (error) => {
23881
+ onCheckoutError?.({
23882
+ message: error.message,
23883
+ error,
23884
+ method: "wallet_connect"
23885
+ });
23886
+ },
23887
+ onWalletDisconnect: handleWalletDisconnect,
23888
+ onWalletConnected: (info, dw) => {
23889
+ setBrowserWalletInfo({ ...info, depositWallet: dw });
23890
+ setStoredWalletState(info.type);
23891
+ setBrowserWalletChainType(dw.chain_type === "solana" ? "solana" : "ethereum");
23892
+ lastCheckoutMethodRef.current = "wallet_connect";
23893
+ },
23894
+ onNewDeposit: () => setView("main"),
23895
+ onDone: () => setView("main"),
23896
+ paymentIntentStatus: paymentIntent.status,
23897
+ onBack: handleBack,
23898
+ onClose: handleClose,
23899
+ defaultSourceChainType,
23900
+ defaultSourceChainId,
23901
+ defaultSourceTokenAddress,
23902
+ defaultSourceSymbol
23903
+ }
23904
+ ),
23905
+ poweredByFooter
23906
+ ]
23028
23907
  }
23029
23908
  )
23030
- ] }) : /* @__PURE__ */ jsx64(SkeletonButton2, {}),
23031
- poweredByFooter
23032
- ] })
23033
- ] }) : view === "wallet_connect" && paymentIntent ? /* @__PURE__ */ jsxs58("div", { className: "uf-flex uf-flex-col uf-gap-1.5", children: [
23034
- /* @__PURE__ */ jsx64(
23035
- WalletConnect,
23036
- {
23037
- walletInfo: browserWalletInfo ?? void 0,
23038
- depositWallet: browserWalletInfo?.depositWallet ?? void 0,
23039
- wallets,
23040
- userId: paymentIntent.user_id || "",
23041
- publishableKey,
23042
- clientSecret,
23043
- prefillAmountUsd: remainingAmountUsd,
23044
- checkoutAmountUsd: paymentIntent.amount_usd,
23045
- checkoutReceivedUsd: paymentIntent.amount_received_usd,
23046
- checkoutDestination: {
23047
- chainType: paymentIntent.destination_chain_type,
23048
- chainId: paymentIntent.destination_chain_id,
23049
- tokenAddress: paymentIntent.destination_token_address,
23050
- decimals: paymentIntent.destination_token_decimals ?? 6
23051
- },
23052
- productType: "payment",
23053
- stablecoinParity: paymentIntent.stablecoin_parity ?? false,
23054
- checkoutRemainingBaseUnits: (() => {
23055
- const remaining = BigInt(paymentIntent.amount) - BigInt(paymentIntent.amount_received);
23056
- return remaining > 0n ? remaining.toString() : "0";
23057
- })(),
23058
- onSuccess: (_txHash) => {
23059
- emitCheckoutSuccess(
23060
- {
23061
- paymentIntentId: paymentIntent.id,
23062
- status: "processing",
23063
- paymentIntent
23064
- },
23065
- "wallet_connect"
23066
- );
23067
- },
23068
- onError: (error) => {
23069
- onCheckoutError?.({
23070
- message: error.message,
23071
- error,
23072
- method: "wallet_connect"
23073
- });
23074
- },
23075
- onWalletDisconnect: handleWalletDisconnect,
23076
- onWalletConnected: (info, dw) => {
23077
- setBrowserWalletInfo({ ...info, depositWallet: dw });
23078
- setStoredWalletState(info.type);
23079
- setBrowserWalletChainType(dw.chain_type === "solana" ? "solana" : "ethereum");
23080
- lastCheckoutMethodRef.current = "wallet_connect";
23081
- },
23082
- onNewDeposit: () => setView("main"),
23083
- onDone: () => setView("main"),
23084
- paymentIntentStatus: paymentIntent.status,
23085
- onBack: handleBack,
23086
- onClose: handleClose,
23087
- defaultSourceChainType,
23088
- defaultSourceChainId,
23089
- defaultSourceTokenAddress,
23090
- defaultSourceSymbol
23091
- }
23092
- ),
23093
- poweredByFooter
23094
- ] }) : null })
23909
+ ) : null
23910
+ }
23911
+ )
23095
23912
  }
23096
23913
  ) }) });
23097
23914
  }
@@ -23591,7 +24408,7 @@ function WithdrawDoubleInput({
23591
24408
  }
23592
24409
 
23593
24410
  // src/components/withdrawals/WithdrawForm.tsx
23594
- import { useState as useState43, useCallback as useCallback10, useMemo as useMemo15, useEffect as useEffect37 } from "react";
24411
+ import { useState as useState43, useCallback as useCallback10, useMemo as useMemo16, useEffect as useEffect37 } from "react";
23595
24412
  import {
23596
24413
  AlertTriangle as AlertTriangle5,
23597
24414
  ArrowUpDown,
@@ -23869,7 +24686,7 @@ async function detectBrowserWallet(chainType, senderAddress) {
23869
24686
  }
23870
24687
 
23871
24688
  // src/hooks/use-hypercore-withdraw-activation.ts
23872
- import { useMemo as useMemo14 } from "react";
24689
+ import { useMemo as useMemo15 } from "react";
23873
24690
  import { ActionType as ActionType6 } from "@unifold/core";
23874
24691
 
23875
24692
  // src/hooks/use-get-deposit-address.ts
@@ -23956,7 +24773,7 @@ function useHypercoreWithdrawActivation(params) {
23956
24773
  actionType: ActionType6.Withdraw,
23957
24774
  enabled: enabled && isHypercore(sourceChainId)
23958
24775
  });
23959
- const depositWalletAddress = useMemo14(() => {
24776
+ const depositWalletAddress = useMemo15(() => {
23960
24777
  const wallets = depositWalletLookup.data?.data ?? [];
23961
24778
  return wallets.find((w) => w.chain_type === sourceChainType)?.address;
23962
24779
  }, [depositWalletLookup.data, sourceChainType]);
@@ -24078,7 +24895,7 @@ function WithdrawForm({
24078
24895
  enabled: debouncedAddress.length > 5 && !!selectedChain
24079
24896
  });
24080
24897
  const isDebouncing = trimmedAddress !== debouncedAddress;
24081
- const addressError = useMemo15(() => {
24898
+ const addressError = useMemo16(() => {
24082
24899
  if (!trimmedAddress || trimmedAddress.length <= 5) return null;
24083
24900
  if (isDebouncing || isVerifyingAddress) return null;
24084
24901
  if (verifyError) return t10.invalidAddress;
@@ -24112,33 +24929,33 @@ function WithdrawForm({
24112
24929
  destinationTokenAddress: selectedChain?.token_address,
24113
24930
  enabled: isAddressValid
24114
24931
  });
24115
- const exchangeRate = useMemo15(() => {
24932
+ const exchangeRate = useMemo16(() => {
24116
24933
  if (!balanceData?.exchangeRate) return 0;
24117
24934
  return parseFloat(balanceData.exchangeRate);
24118
24935
  }, [balanceData]);
24119
- const balanceCrypto = useMemo15(() => {
24936
+ const balanceCrypto = useMemo16(() => {
24120
24937
  if (!balanceData?.balanceHuman) return 0;
24121
24938
  return parseFloat(balanceData.balanceHuman);
24122
24939
  }, [balanceData]);
24123
- const balanceUsdNum = useMemo15(() => {
24940
+ const balanceUsdNum = useMemo16(() => {
24124
24941
  if (!balanceData?.balanceUsd) return 0;
24125
24942
  return parseFloat(balanceData.balanceUsd);
24126
24943
  }, [balanceData]);
24127
24944
  const tokenSymbol = sourceTokenSymbol || balanceData?.symbol || "TOKEN";
24128
24945
  const sourceDecimals = balanceData?.decimals ?? 6;
24129
- const cryptoAmountFromInput = useMemo15(() => {
24946
+ const cryptoAmountFromInput = useMemo16(() => {
24130
24947
  const val = parseFloat(amount);
24131
24948
  if (!val || val <= 0) return 0;
24132
24949
  if (inputUnit === "crypto") return val;
24133
24950
  return exchangeRate > 0 ? val / exchangeRate : 0;
24134
24951
  }, [amount, inputUnit, exchangeRate]);
24135
- const fiatAmountFromInput = useMemo15(() => {
24952
+ const fiatAmountFromInput = useMemo16(() => {
24136
24953
  const val = parseFloat(amount);
24137
24954
  if (!val || val <= 0) return 0;
24138
24955
  if (inputUnit === "fiat") return val;
24139
24956
  return val * exchangeRate;
24140
24957
  }, [amount, inputUnit, exchangeRate]);
24141
- const convertedDisplay = useMemo15(() => {
24958
+ const convertedDisplay = useMemo16(() => {
24142
24959
  if (!amount || parseFloat(amount) <= 0) return null;
24143
24960
  if (inputUnit === "crypto") {
24144
24961
  return `$${fiatAmountFromInput.toLocaleString(void 0, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`;
@@ -24157,7 +24974,7 @@ function WithdrawForm({
24157
24974
  isMaxed,
24158
24975
  isStablecoin
24159
24976
  ]);
24160
- const balanceDisplay = useMemo15(() => {
24977
+ const balanceDisplay = useMemo16(() => {
24161
24978
  if (isLoadingBalance || !balanceData) return null;
24162
24979
  if (inputUnit === "crypto") {
24163
24980
  const displayDecimals = isStablecoin ? 2 : 6;
@@ -25340,7 +26157,7 @@ function WithdrawModal({
25340
26157
  }
25341
26158
 
25342
26159
  // src/components/withdrawals/WithdrawTokenSelector.tsx
25343
- import { useState as useState46, useMemo as useMemo16 } from "react";
26160
+ import { useState as useState46, useMemo as useMemo17 } from "react";
25344
26161
  import { Search } from "lucide-react";
25345
26162
  import Fuse2 from "fuse.js";
25346
26163
  import { jsx as jsx70, jsxs as jsxs64 } from "react/jsx-runtime";
@@ -25349,7 +26166,7 @@ function WithdrawTokenSelector({ tokens, onSelect, onBack }) {
25349
26166
  const { themeClass, colors: colors2, fonts, components } = useTheme();
25350
26167
  const [searchQuery, setSearchQuery] = useState46("");
25351
26168
  const [hoveredKey, setHoveredKey] = useState46(null);
25352
- const allOptions = useMemo16(() => {
26169
+ const allOptions = useMemo17(() => {
25353
26170
  const options = [];
25354
26171
  tokens.forEach((token) => {
25355
26172
  token.chains.forEach((chain) => {
@@ -25358,7 +26175,7 @@ function WithdrawTokenSelector({ tokens, onSelect, onBack }) {
25358
26175
  });
25359
26176
  return options;
25360
26177
  }, [tokens]);
25361
- const fuse = useMemo16(
26178
+ const fuse = useMemo17(
25362
26179
  () => new Fuse2(allOptions, {
25363
26180
  keys: [
25364
26181
  { name: "token.symbol", weight: 2 },
@@ -25371,7 +26188,7 @@ function WithdrawTokenSelector({ tokens, onSelect, onBack }) {
25371
26188
  }),
25372
26189
  [allOptions]
25373
26190
  );
25374
- const filteredOptions = useMemo16(() => {
26191
+ const filteredOptions = useMemo17(() => {
25375
26192
  if (!searchQuery.trim()) return allOptions;
25376
26193
  const query = searchQuery.trim();
25377
26194
  const results = fuse.search(query);
@@ -25616,7 +26433,6 @@ export {
25616
26433
  getStoredApplePaySession,
25617
26434
  isHypercoreChain,
25618
26435
  isOnrampTokenFresh,
25619
- isPhoneVerificationFresh,
25620
26436
  mergeColors,
25621
26437
  resolveComponentTokens,
25622
26438
  sendEvmWithdraw,