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

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
 
@@ -1487,7 +1487,7 @@ var en_default = {
1487
1487
  payWithStripeLink: {
1488
1488
  success: {
1489
1489
  processingTitle: "Payment confirmed",
1490
- processingSubtitle: "We\u2019ve received your payment and are preparing your purchase. This will only take a moment.",
1490
+ processingSubtitle: "We\u2019ve received your payment and are sending your purchase to your wallet. This usually takes just a few minutes.",
1491
1491
  completeTitle: "Your purchase is on the way",
1492
1492
  completeSubtitle: "Your purchase is complete and is being sent to your wallet.",
1493
1493
  failedTitle: "Purchase didn\u2019t go through",
@@ -3520,37 +3520,21 @@ function BuyWithCard({
3520
3520
 
3521
3521
  // src/components/deposits/BuyWithApplePay.tsx
3522
3522
  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";
3523
+ import { useCallback, useEffect as useEffect7, useMemo as useMemo3, useRef as useRef3, useState as useState11 } from "react";
3524
+ import { Loader2 as Loader22 } from "lucide-react";
3525
3525
  import {
3526
3526
  createCoinbaseApplePaySession,
3527
3527
  createOnrampVerificationSession,
3528
3528
  exchangeOnrampVerificationToken,
3529
3529
  sendOnrampVerificationOtp,
3530
3530
  verifyOnrampVerificationOtp,
3531
+ getCoinbaseApplePayLimits as getCoinbaseApplePayLimits2,
3532
+ requestCoinbaseApplePayLimitUpgrade,
3531
3533
  generatePrefixedKSUID as generatePrefixedKSUID3,
3532
3534
  getWalletByChainType as getWalletByChainType2,
3533
3535
  DepositEventType as DepositEventType3
3534
3536
  } from "@unifold/core";
3535
3537
 
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
3538
  // src/lib/apple-pay.ts
3555
3539
  var STORAGE_KEY_PREFIX = "unifold:apple-pay:session";
3556
3540
  var PHONE_FRESH_MS = 55 * 24 * 60 * 60 * 1e3;
@@ -3586,12 +3570,6 @@ function clearStoredApplePaySession(userId) {
3586
3570
  } catch {
3587
3571
  }
3588
3572
  }
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
3573
  var ONRAMP_TOKEN_EXPIRY_MARGIN_MS = 6e4;
3596
3574
  function isOnrampTokenFresh(contact) {
3597
3575
  if (!contact?.onrampToken || !contact.onrampTokenExpiresAtMs) return false;
@@ -3599,13 +3577,13 @@ function isOnrampTokenFresh(contact) {
3599
3577
  }
3600
3578
 
3601
3579
  // src/hooks/use-coinbase-legal-agreements.ts
3602
- import { useQuery as useQuery4 } from "@tanstack/react-query";
3580
+ import { useQuery as useQuery3 } from "@tanstack/react-query";
3603
3581
  import { getCoinbaseLegalAgreements } from "@unifold/core";
3604
3582
  function useCoinbaseLegalAgreements({
3605
3583
  publishableKey,
3606
3584
  enabled = true
3607
3585
  }) {
3608
- return useQuery4({
3586
+ return useQuery3({
3609
3587
  queryKey: ["unifold", "coinbaseLegalAgreements", publishableKey],
3610
3588
  queryFn: () => getCoinbaseLegalAgreements(publishableKey),
3611
3589
  enabled: enabled && !!publishableKey,
@@ -3617,49 +3595,215 @@ function useCoinbaseLegalAgreements({
3617
3595
  });
3618
3596
  }
3619
3597
 
3598
+ // src/hooks/use-apple-pay-initial-screen.ts
3599
+ import { useMemo as useMemo2 } from "react";
3600
+
3601
+ // src/hooks/use-apple-pay-limits.ts
3602
+ import { useQuery as useQuery4 } from "@tanstack/react-query";
3603
+ import { getCoinbaseApplePayLimits } from "@unifold/core";
3604
+ var US_E164_REGEX = /^\+1\d{10}$/;
3605
+ function useApplePayLimits({
3606
+ phone,
3607
+ publishableKey,
3608
+ enabled = true
3609
+ }) {
3610
+ const phoneValid = US_E164_REGEX.test(phone);
3611
+ return useQuery4({
3612
+ queryKey: ["unifold", "applePayLimits", phone, publishableKey],
3613
+ queryFn: ({ signal }) => getCoinbaseApplePayLimits(phone, publishableKey, signal),
3614
+ enabled: enabled && phoneValid && !!publishableKey,
3615
+ // Coinbase's caps don't change second-to-second; 30s is plenty fresh
3616
+ // for the "user opened the modal, edited their phone, opened it
3617
+ // again" usage pattern, while keeping us off their API on rapid
3618
+ // tap-throughs.
3619
+ staleTime: 3e4,
3620
+ gcTime: 5 * 6e4,
3621
+ refetchOnWindowFocus: false,
3622
+ retry: false
3623
+ });
3624
+ }
3625
+
3626
+ // src/hooks/use-apple-pay-initial-screen.ts
3627
+ var PHONE_REGEX = /^\+1\d{10}$/;
3628
+ var EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
3629
+ function getViewForLimitStatus(limits) {
3630
+ if (!limits.limit_reached) return void 0;
3631
+ switch (limits.upgrade_status) {
3632
+ case "unrequested":
3633
+ return "limit_upgrade_intro";
3634
+ case "resubmit":
3635
+ return "limit_upgrade_form";
3636
+ case "pending":
3637
+ return "limit_upgrade_submitting";
3638
+ case "active":
3639
+ return void 0;
3640
+ default:
3641
+ return "guest_limit_reached";
3642
+ }
3643
+ }
3644
+ function useApplePayInitialScreen({
3645
+ userId,
3646
+ publishableKey,
3647
+ normalizedPhone,
3648
+ userEmail,
3649
+ sessionPhoneVerified,
3650
+ isWaiting
3651
+ }) {
3652
+ const initialStoredSession = useMemo2(() => getStoredApplePaySession(userId), [userId]);
3653
+ const phoneVerified = PHONE_REGEX.test(normalizedPhone) && (sessionPhoneVerified || initialStoredSession?.phone === normalizedPhone && !!initialStoredSession?.phoneVerifiedAt);
3654
+ const {
3655
+ data: applePayLimits,
3656
+ isLoading: applePayLimitsLoading,
3657
+ refetch: refetchApplePayLimits
3658
+ } = useApplePayLimits({
3659
+ phone: normalizedPhone,
3660
+ publishableKey,
3661
+ enabled: phoneVerified
3662
+ });
3663
+ const action = useMemo2(() => {
3664
+ if (isWaiting || applePayLimitsLoading) return { kind: "pending" };
3665
+ const stored = initialStoredSession;
3666
+ const hasUserEmail = !!userEmail && EMAIL_REGEX.test(userEmail);
3667
+ if (!stored) {
3668
+ return { kind: "navigate", view: hasUserEmail ? "phone_input" : "email_input" };
3669
+ }
3670
+ if (hasUserEmail && stored.email.toLowerCase() !== userEmail.toLowerCase()) {
3671
+ return { kind: "navigate", view: "phone_input" };
3672
+ }
3673
+ if (applePayLimits) {
3674
+ const limitView = getViewForLimitStatus(applePayLimits);
3675
+ if (limitView) {
3676
+ return { kind: "limit_upgrade", view: limitView, status: applePayLimits.upgrade_status };
3677
+ }
3678
+ }
3679
+ if (isOnrampTokenFresh(stored) && stored.onrampToken && stored.onrampTokenExpiresAtMs) {
3680
+ return {
3681
+ kind: "resume_with_token",
3682
+ token: {
3683
+ token: stored.onrampToken,
3684
+ token_type: "Bearer",
3685
+ expires_in: Math.max(0, Math.floor((stored.onrampTokenExpiresAtMs - Date.now()) / 1e3))
3686
+ }
3687
+ };
3688
+ }
3689
+ return { kind: "resume_send_sms" };
3690
+ }, [isWaiting, applePayLimitsLoading, initialStoredSession, userEmail, applePayLimits]);
3691
+ return {
3692
+ action,
3693
+ applePayLimits,
3694
+ applePayLimitsLoading,
3695
+ refetchApplePayLimits,
3696
+ phoneVerified
3697
+ };
3698
+ }
3699
+
3700
+ // src/hooks/use-default-onramp-token.ts
3701
+ import { useQuery as useQuery5 } from "@tanstack/react-query";
3702
+ import { getDefaultOnrampToken as getDefaultOnrampToken2 } from "@unifold/core";
3703
+ function useDefaultOnrampToken({
3704
+ publishableKey,
3705
+ tokenAddress,
3706
+ chainId,
3707
+ chainType,
3708
+ countryCode,
3709
+ subdivisionCode,
3710
+ enabled = true
3711
+ }) {
3712
+ const {
3713
+ data: defaultToken,
3714
+ isLoading,
3715
+ isError,
3716
+ error
3717
+ } = useQuery5({
3718
+ queryKey: [
3719
+ "unifold",
3720
+ "defaultOnrampToken",
3721
+ publishableKey,
3722
+ tokenAddress,
3723
+ chainId,
3724
+ chainType,
3725
+ countryCode,
3726
+ subdivisionCode
3727
+ ],
3728
+ queryFn: () => getDefaultOnrampToken2(
3729
+ {
3730
+ token_address: tokenAddress,
3731
+ chain_id: chainId,
3732
+ chain_type: chainType,
3733
+ country_code: countryCode,
3734
+ subdivision_code: subdivisionCode
3735
+ },
3736
+ publishableKey
3737
+ ),
3738
+ enabled: enabled && !!tokenAddress && !!chainId && !!chainType,
3739
+ staleTime: 1e3 * 60 * 60,
3740
+ refetchOnMount: false,
3741
+ refetchOnWindowFocus: false
3742
+ });
3743
+ return { defaultToken, isLoading, isError, error: error ?? null };
3744
+ }
3745
+
3620
3746
  // src/components/deposits/BuyWithApplePay.tsx
3621
3747
  import { Fragment as Fragment4, jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
3622
3748
  var COINBASE_PAY_ORIGIN = "https://pay.coinbase.com";
3749
+ var COINBASE_ALLOWED_POSTMESSAGE_ORIGINS = /* @__PURE__ */ new Set([
3750
+ COINBASE_PAY_ORIGIN,
3751
+ "https://www.coinbase.com"
3752
+ ]);
3753
+ var UNIFOLD_SUPPORT_URL = "https://unifold.io/support";
3623
3754
  var DEFAULT_MIN_USD = 5;
3624
3755
  var DEFAULT_MAX_USD = 500;
3625
3756
  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}`;
3757
+ var PHONE_REGEX2 = /^\+1\d{10}$/;
3758
+ var EMAIL_REGEX2 = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
3759
+ function parseCoinbasePostMessage(raw) {
3760
+ const parseString = (s) => {
3761
+ try {
3762
+ return JSON.parse(s);
3763
+ } catch {
3764
+ return null;
3765
+ }
3766
+ };
3767
+ const asObject = (value) => value && typeof value === "object" ? value : null;
3768
+ let candidate = raw;
3769
+ if (typeof candidate === "string") {
3770
+ candidate = parseString(candidate);
3632
3771
  }
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)}`;
3772
+ const obj = asObject(candidate);
3773
+ if (!obj) return null;
3774
+ const nestedData = obj.data;
3775
+ let eventName = typeof obj.eventName === "string" && obj.eventName || typeof obj.event_name === "string" && obj.event_name || typeof obj.type === "string" && obj.type || null;
3776
+ let dataObj = asObject(nestedData);
3777
+ if (!eventName && typeof nestedData === "string") {
3778
+ const nestedParsed = parseString(nestedData);
3779
+ const nestedObj = asObject(nestedParsed);
3780
+ if (nestedObj) {
3781
+ eventName = typeof nestedObj.eventName === "string" && nestedObj.eventName || typeof nestedObj.event_name === "string" && nestedObj.event_name || typeof nestedObj.type === "string" && nestedObj.type || null;
3782
+ dataObj = asObject(nestedObj.data);
3783
+ }
3784
+ }
3785
+ if (!eventName || !/^onramp_api\./i.test(eventName)) return null;
3786
+ return {
3787
+ eventName,
3788
+ data: dataObj ? {
3789
+ errorCode: typeof dataObj.errorCode === "string" ? dataObj.errorCode : typeof dataObj.error_code === "string" ? dataObj.error_code : void 0,
3790
+ errorMessage: typeof dataObj.errorMessage === "string" ? dataObj.errorMessage : typeof dataObj.error_message === "string" ? dataObj.error_message : void 0
3791
+ } : void 0
3792
+ };
3646
3793
  }
3647
3794
  var BuyWithApplePay = React5.forwardRef(
3648
3795
  function BuyWithApplePay2({
3649
3796
  userId,
3650
3797
  publishableKey,
3651
- recipientAddress,
3652
3798
  destinationChainType,
3653
3799
  destinationChainId,
3654
3800
  destinationTokenAddress,
3655
- destinationTokenSymbol = "USDC",
3656
- externalUserEmail,
3657
- enableApplePay,
3801
+ userEmail,
3658
3802
  amountUsd,
3659
3803
  minAmountUsd = DEFAULT_MIN_USD,
3660
3804
  maxAmountUsd = DEFAULT_MAX_USD,
3661
3805
  suggestedAmountsUsd = DEFAULT_SUGGESTED,
3662
- wallets: externalWallets,
3806
+ wallets: depositWallets,
3663
3807
  onDepositSuccess,
3664
3808
  onDepositError,
3665
3809
  onEvent,
@@ -3668,48 +3812,61 @@ var BuyWithApplePay = React5.forwardRef(
3668
3812
  onExit
3669
3813
  }, ref) {
3670
3814
  const { colors: colors2, fonts, components } = useTheme();
3671
- const { projectConfig, isLoading: configLoading } = useProjectConfig({ publishableKey });
3672
- const applePayEnabled = enableApplePay ?? projectConfig?.apple_pay?.enabled ?? true;
3673
3815
  const [view, setView] = useState11("loading_config");
3674
3816
  const [errorMessage, setErrorMessage] = useState11("");
3675
- const [email, setEmail] = useState11(externalUserEmail ?? "");
3676
- const [phoneInput, setPhoneInput] = useState11("");
3677
- const [, setTosAccepted] = useState11(false);
3817
+ const [email, setEmail] = useState11(() => {
3818
+ if (userEmail && EMAIL_REGEX2.test(userEmail)) return userEmail;
3819
+ return getStoredApplePaySession(userId)?.email ?? "";
3820
+ });
3821
+ const [phoneInput, setPhoneInput] = useState11(() => {
3822
+ const stored = getStoredApplePaySession(userId);
3823
+ if (!stored) return "";
3824
+ if (userEmail && EMAIL_REGEX2.test(userEmail) && stored.email.toLowerCase() !== userEmail.toLowerCase()) {
3825
+ return "";
3826
+ }
3827
+ return stored.phone;
3828
+ });
3678
3829
  const { data: legalAgreementsData, isLoading: legalAgreementsLoading } = useCoinbaseLegalAgreements({
3679
- publishableKey,
3680
- enabled: applePayEnabled
3830
+ publishableKey
3681
3831
  });
3682
3832
  const legalAgreements = legalAgreementsData ?? null;
3683
3833
  const [verificationSession, setVerificationSession] = useState11(null);
3684
3834
  const [clientSecret, setClientSecret] = useState11(null);
3685
- const [emailCode, setEmailCode] = useState11("");
3686
3835
  const [phoneCode, setPhoneCode] = useState11("");
3687
3836
  const [otpError, setOtpError] = useState11(null);
3688
3837
  const [otpSubmitting, setOtpSubmitting] = useState11(false);
3689
3838
  const [resendCooldown, setResendCooldown] = useState11(0);
3839
+ const [dobInput, setDobInput] = useState11("");
3840
+ const [ssnLast4, setSsnLast4] = useState11("");
3841
+ const [limitUpgradeError, setLimitUpgradeError] = useState11(null);
3842
+ const [upgradeSubmitting, setUpgradeSubmitting] = useState11(false);
3843
+ const upgradeFlowAbortRef = useRef3(null);
3690
3844
  const [amount, setAmount] = useState11(amountUsd ?? "");
3691
3845
  const [paymentSession, setPaymentSession] = useState11(
3692
3846
  null
3693
3847
  );
3694
3848
  const [paymentSessionAmount, setPaymentSessionAmount] = useState11(null);
3695
- const [preparingSession, setPreparingSession] = useState11(false);
3696
3849
  const popupRef = useRef3(null);
3697
3850
  const sessionAbortRef = useRef3(null);
3698
3851
  const [onrampToken, setOnrampToken] = useState11(null);
3699
- const { data: depositAddressResponse } = useDepositAddress({
3700
- userId,
3852
+ const { userIpInfo, isLoading: userIpInfoLoading } = useUserIp();
3853
+ const {
3854
+ defaultToken,
3855
+ isLoading: defaultTokenLoading,
3856
+ isError: defaultTokenError
3857
+ } = useDefaultOnrampToken({
3701
3858
  publishableKey,
3702
- recipientAddress,
3703
- destinationChainType,
3704
- destinationChainId,
3705
- destinationTokenAddress,
3706
- enabled: !externalWallets?.length
3859
+ tokenAddress: destinationTokenAddress,
3860
+ chainId: destinationChainId,
3861
+ chainType: destinationChainType,
3862
+ countryCode: userIpInfo?.alpha2,
3863
+ subdivisionCode: userIpInfo?.subdivisionCode ?? void 0
3707
3864
  });
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]);
3865
+ const depositWallet = useMemo3(() => {
3866
+ const routingChainType = defaultToken?.destination_token_metadata?.chain_type;
3867
+ if (!routingChainType) return void 0;
3868
+ return getWalletByChainType2(depositWallets ?? [], routingChainType);
3869
+ }, [depositWallets, defaultToken]);
3713
3870
  const depositWalletId = depositWallet?.id;
3714
3871
  const { executions } = useDepositPolling({
3715
3872
  userId,
@@ -3733,49 +3890,10 @@ var BuyWithApplePay = React5.forwardRef(
3733
3890
  onDepositError(error);
3734
3891
  } : void 0
3735
3892
  });
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]);
3893
+ const [emailLocked, setEmailLocked] = useState11(() => {
3894
+ if (userEmail && EMAIL_REGEX2.test(userEmail)) return true;
3895
+ return !!getStoredApplePaySession(userId);
3896
+ });
3779
3897
  useEffect7(() => {
3780
3898
  if (resendCooldown <= 0) return;
3781
3899
  const t13 = setInterval(() => setResendCooldown((c) => Math.max(0, c - 1)), 1e3);
@@ -3784,19 +3902,10 @@ var BuyWithApplePay = React5.forwardRef(
3784
3902
  useEffect7(() => {
3785
3903
  if (view !== "checking_deposit") return;
3786
3904
  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
- }
3905
+ if (!COINBASE_ALLOWED_POSTMESSAGE_ORIGINS.has(event.origin)) return;
3906
+ const parsed = parseCoinbasePostMessage(event.data);
3799
3907
  if (!parsed) return;
3908
+ const eventName = parsed.eventName.toLowerCase();
3800
3909
  const code = parsed.data?.errorCode;
3801
3910
  const closePopup = () => {
3802
3911
  try {
@@ -3805,16 +3914,20 @@ var BuyWithApplePay = React5.forwardRef(
3805
3914
  }
3806
3915
  popupRef.current = null;
3807
3916
  };
3808
- switch (parsed.eventName) {
3917
+ switch (eventName) {
3809
3918
  case "onramp_api.commit_success":
3810
3919
  case "onramp_api.polling_success":
3920
+ case "onramp_api.cancel":
3811
3921
  closePopup();
3812
3922
  break;
3813
3923
  case "onramp_api.commit_error":
3924
+ case "onramp_api.polling_failed":
3814
3925
  if (code === "ERROR_CODE_GUEST_TRANSACTION_COUNT" || code === "ERROR_CODE_GUEST_DAILY_TRANSACTION_COUNT" || code === "ERROR_CODE_GUEST_TRANSACTION_LIMIT") {
3815
3926
  closePopup();
3816
3927
  setView("guest_limit_reached");
3928
+ return;
3817
3929
  }
3930
+ closePopup();
3818
3931
  break;
3819
3932
  default:
3820
3933
  break;
@@ -3826,17 +3939,17 @@ var BuyWithApplePay = React5.forwardRef(
3826
3939
  useEffect7(() => {
3827
3940
  onViewChange?.(view);
3828
3941
  }, [view, onViewChange]);
3829
- useEffect7(
3830
- () => () => {
3942
+ useEffect7(() => {
3943
+ return () => {
3831
3944
  sessionAbortRef.current?.abort();
3945
+ upgradeFlowAbortRef.current?.abort();
3832
3946
  try {
3833
3947
  popupRef.current?.close();
3834
3948
  } catch {
3835
3949
  }
3836
3950
  popupRef.current = null;
3837
- },
3838
- []
3839
- );
3951
+ };
3952
+ }, []);
3840
3953
  React5.useImperativeHandle(
3841
3954
  ref,
3842
3955
  () => ({
@@ -3846,14 +3959,20 @@ var BuyWithApplePay = React5.forwardRef(
3846
3959
  setView("email_input");
3847
3960
  return true;
3848
3961
  }
3849
- if (view === "email_otp") {
3850
- setView("phone_input");
3851
- setEmailCode("");
3852
- return true;
3853
- }
3854
3962
  if (view === "phone_otp") {
3855
3963
  setView("phone_input");
3856
3964
  setPhoneCode("");
3965
+ setOtpSubmitting(false);
3966
+ return true;
3967
+ }
3968
+ if (view === "limit_upgrade_intro" || view === "limit_upgrade_failed") {
3969
+ return false;
3970
+ }
3971
+ if (view === "limit_upgrade_form") {
3972
+ setView("limit_upgrade_intro");
3973
+ return true;
3974
+ }
3975
+ if (view === "limit_upgrade_submitting") {
3857
3976
  return true;
3858
3977
  }
3859
3978
  if (view === "checking_deposit") {
@@ -3873,22 +3992,21 @@ var BuyWithApplePay = React5.forwardRef(
3873
3992
  }),
3874
3993
  [view, emailLocked]
3875
3994
  );
3876
- const normalizedPhone = useMemo2(() => formatPhoneInput(phoneInput), [phoneInput]);
3877
- const isContactValid = EMAIL_REGEX.test(email) && PHONE_REGEX.test(normalizedPhone);
3878
- const persistContact = useCallback(
3995
+ const normalizedPhone = useMemo3(() => formatPhoneInput(phoneInput), [phoneInput]);
3996
+ const isContactValid = EMAIL_REGEX2.test(email) && PHONE_REGEX2.test(normalizedPhone);
3997
+ const storeApplePaySession = useCallback(
3879
3998
  (patch = {}) => {
3880
- const base = {
3999
+ const session = {
3881
4000
  email,
3882
4001
  phone: normalizedPhone,
3883
4002
  termsAcceptedAt: (/* @__PURE__ */ new Date()).toISOString(),
3884
4003
  ...patch
3885
4004
  };
3886
- setStoredApplePaySession(userId, base);
4005
+ setStoredApplePaySession(userId, session);
3887
4006
  },
3888
4007
  [email, normalizedPhone, userId]
3889
4008
  );
3890
- const startVerification = useCallback(async () => {
3891
- if (!isContactValid) return;
4009
+ const createSessionAndSendOtp = useCallback(async () => {
3892
4010
  setView("submitting_session");
3893
4011
  setErrorMessage("");
3894
4012
  try {
@@ -3906,71 +4024,192 @@ var BuyWithApplePay = React5.forwardRef(
3906
4024
  throw new Error("Server did not return a client_secret");
3907
4025
  }
3908
4026
  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
- }
4027
+ storeApplePaySession();
4028
+ await sendOnrampVerificationOtp(session.id, "phone", session.client_secret, publishableKey);
4029
+ setResendCooldown(30);
4030
+ setView("phone_otp");
3929
4031
  } catch (err) {
3930
4032
  const raw = err instanceof Error ? err.message : "Failed to start verification";
3931
4033
  setErrorMessage(prettifyVerificationError(raw));
3932
4034
  setView("phone_input");
3933
4035
  }
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);
4036
+ }, [email, normalizedPhone, publishableKey, storeApplePaySession]);
4037
+ const clearUpgradeFields = useCallback(() => {
4038
+ setSsnLast4("");
4039
+ setDobInput("");
4040
+ }, []);
4041
+ const { action: initialAction, refetchApplePayLimits } = useApplePayInitialScreen({
4042
+ userId,
4043
+ publishableKey,
4044
+ normalizedPhone,
4045
+ userEmail,
4046
+ sessionPhoneVerified: verificationSession?.phone?.status === "verified",
4047
+ isWaiting: defaultTokenLoading || legalAgreementsLoading || userIpInfoLoading
4048
+ });
4049
+ const pollLimitUpgradeStatus = useCallback(
4050
+ async (signal) => {
4051
+ const POLL_INTERVAL_MS4 = 1500;
4052
+ const POLL_TIMEOUT_MS = 3e4;
4053
+ const startedAt = Date.now();
4054
+ let firstIteration = true;
4055
+ while (Date.now() - startedAt < POLL_TIMEOUT_MS) {
4056
+ if (signal.aborted) return;
4057
+ if (!firstIteration) {
4058
+ await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS4));
4059
+ }
4060
+ firstIteration = false;
4061
+ if (signal.aborted) return;
4062
+ let fresh;
4063
+ try {
4064
+ fresh = await getCoinbaseApplePayLimits2(normalizedPhone, publishableKey, signal);
4065
+ } catch {
4066
+ if (signal.aborted) return;
4067
+ continue;
4068
+ }
4069
+ const { upgrade_status: status, limit_reached: stillCapped } = fresh;
4070
+ if (status === "active" || !stillCapped) {
4071
+ await refetchApplePayLimits();
4072
+ clearUpgradeFields();
4073
+ setLimitUpgradeError(null);
4074
+ if (onrampToken) {
4075
+ setView("amount");
4076
+ } else {
4077
+ if (signal.aborted) return;
4078
+ await createSessionAndSendOtp();
4079
+ }
4080
+ return;
4081
+ }
4082
+ if (status === "inactive") {
4083
+ await refetchApplePayLimits();
4084
+ clearUpgradeFields();
4085
+ setLimitUpgradeError(null);
4086
+ setView("guest_limit_reached");
4087
+ return;
4088
+ }
4089
+ if (status === "resubmit") {
4090
+ await refetchApplePayLimits();
4091
+ setSsnLast4("");
4092
+ setLimitUpgradeError(
4093
+ "We couldn't verify your information. Please check your SSN and date of birth and try again."
4094
+ );
4095
+ setView("limit_upgrade_form");
4096
+ return;
4097
+ }
4098
+ }
4099
+ setLimitUpgradeError(null);
4100
+ setView("limit_upgrade_failed");
4101
+ },
4102
+ [
4103
+ normalizedPhone,
4104
+ publishableKey,
4105
+ refetchApplePayLimits,
4106
+ onrampToken,
4107
+ createSessionAndSendOtp,
4108
+ clearUpgradeFields
4109
+ ]
4110
+ );
4111
+ const startUpgradePolling = useCallback(async () => {
4112
+ upgradeFlowAbortRef.current?.abort();
4113
+ const controller = new AbortController();
4114
+ upgradeFlowAbortRef.current = controller;
3940
4115
  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");
4116
+ await pollLimitUpgradeStatus(controller.signal);
4117
+ } finally {
4118
+ if (upgradeFlowAbortRef.current === controller) {
4119
+ upgradeFlowAbortRef.current = null;
4120
+ }
4121
+ }
4122
+ }, [pollLimitUpgradeStatus]);
4123
+ const applyLimitRouting = useCallback(
4124
+ (nextView, status) => {
4125
+ if (nextView === "limit_upgrade_form" && status === "resubmit") {
4126
+ setLimitUpgradeError(
4127
+ "We couldn't verify your information. Please check your SSN and date of birth and try again."
4128
+ );
3955
4129
  } else {
3956
- setOtpError("Incorrect code. Please try again.");
3957
- setEmailCode("");
4130
+ setLimitUpgradeError(null);
3958
4131
  }
3959
- } catch (err) {
3960
- setOtpError(
3961
- prettifyVerificationError(err instanceof Error ? err.message : "Verification failed")
3962
- );
3963
- setEmailCode("");
4132
+ setView(nextView);
4133
+ if (nextView === "limit_upgrade_submitting") {
4134
+ void startUpgradePolling();
4135
+ }
4136
+ },
4137
+ [startUpgradePolling]
4138
+ );
4139
+ const hasInitializedRef = useRef3(false);
4140
+ useEffect7(() => {
4141
+ if (hasInitializedRef.current) return;
4142
+ if (initialAction.kind === "pending") return;
4143
+ hasInitializedRef.current = true;
4144
+ switch (initialAction.kind) {
4145
+ case "navigate":
4146
+ setView(initialAction.view);
4147
+ break;
4148
+ case "limit_upgrade":
4149
+ applyLimitRouting(initialAction.view, initialAction.status);
4150
+ break;
4151
+ case "resume_with_token":
4152
+ setOnrampToken(initialAction.token);
4153
+ setView("amount");
4154
+ break;
4155
+ case "resume_send_sms":
4156
+ void createSessionAndSendOtp();
4157
+ break;
4158
+ }
4159
+ }, [initialAction, applyLimitRouting, createSessionAndSendOtp]);
4160
+ const startVerification = useCallback(async () => {
4161
+ if (!isContactValid) return;
4162
+ await createSessionAndSendOtp();
4163
+ }, [isContactValid, createSessionAndSendOtp]);
4164
+ const submitLimitUpgrade = useCallback(async () => {
4165
+ if (upgradeSubmitting) return;
4166
+ try {
4167
+ const dob = parseDobInput(dobInput);
4168
+ if (!dob || !/^\d{4}$/.test(ssnLast4)) return;
4169
+ setUpgradeSubmitting(true);
4170
+ setLimitUpgradeError(null);
4171
+ setView("limit_upgrade_submitting");
4172
+ const requestController = new AbortController();
4173
+ upgradeFlowAbortRef.current?.abort();
4174
+ upgradeFlowAbortRef.current = requestController;
4175
+ try {
4176
+ await requestCoinbaseApplePayLimitUpgrade(
4177
+ {
4178
+ phone: normalizedPhone,
4179
+ fields: { ssnLast4, dateOfBirth: dob }
4180
+ },
4181
+ publishableKey,
4182
+ requestController.signal
4183
+ );
4184
+ } catch (err) {
4185
+ if (requestController.signal.aborted) return;
4186
+ const raw = err instanceof Error ? err.message : "Failed to request limit upgrade";
4187
+ if (isLimitUpgradeTerminalBlock(raw)) {
4188
+ clearUpgradeFields();
4189
+ setLimitUpgradeError(null);
4190
+ setView("guest_limit_reached");
4191
+ return;
4192
+ }
4193
+ setLimitUpgradeError(prettifyVerificationError(raw));
4194
+ setView("limit_upgrade_form");
4195
+ return;
4196
+ } finally {
4197
+ if (upgradeFlowAbortRef.current === requestController) {
4198
+ upgradeFlowAbortRef.current = null;
4199
+ }
4200
+ }
4201
+ await startUpgradePolling();
3964
4202
  } finally {
3965
- setOtpSubmitting(false);
4203
+ setUpgradeSubmitting(false);
3966
4204
  }
3967
4205
  }, [
3968
- verificationSession,
3969
- clientSecret,
3970
- emailCode,
4206
+ upgradeSubmitting,
4207
+ dobInput,
4208
+ ssnLast4,
4209
+ normalizedPhone,
3971
4210
  publishableKey,
3972
- persistContact,
3973
- otpSubmitting
4211
+ startUpgradePolling,
4212
+ clearUpgradeFields
3974
4213
  ]);
3975
4214
  const submitPhoneOtp = useCallback(async () => {
3976
4215
  if (!verificationSession || !clientSecret || phoneCode.length !== 6) return;
@@ -3993,7 +4232,7 @@ var BuyWithApplePay = React5.forwardRef(
3993
4232
  publishableKey
3994
4233
  );
3995
4234
  setOnrampToken(tokenRes);
3996
- persistContact({
4235
+ storeApplePaySession({
3997
4236
  emailVerifiedAt: updated.email.verified_at,
3998
4237
  phoneVerifiedAt: updated.phone.verified_at,
3999
4238
  onrampToken: tokenRes.token,
@@ -4002,17 +4241,30 @@ var BuyWithApplePay = React5.forwardRef(
4002
4241
  setEmailLocked(true);
4003
4242
  setPhoneCode("");
4004
4243
  if (amountUsd) setAmount(amountUsd);
4005
- setView("amount");
4006
- } else {
4007
- setOtpError("Incorrect code. Please try again.");
4008
- setPhoneCode("");
4244
+ try {
4245
+ const result = await refetchApplePayLimits();
4246
+ if (result.data) {
4247
+ const nextView = getViewForLimitStatus(result.data);
4248
+ if (nextView) {
4249
+ setOtpSubmitting(false);
4250
+ applyLimitRouting(nextView, result.data.upgrade_status);
4251
+ return;
4252
+ }
4253
+ }
4254
+ } catch {
4255
+ }
4256
+ setOtpSubmitting(false);
4257
+ setView("amount");
4258
+ return;
4009
4259
  }
4260
+ setOtpError("Incorrect code. Please try again.");
4261
+ setPhoneCode("");
4262
+ setOtpSubmitting(false);
4010
4263
  } catch (err) {
4011
4264
  setOtpError(
4012
4265
  prettifyVerificationError(err instanceof Error ? err.message : "Verification failed")
4013
4266
  );
4014
4267
  setPhoneCode("");
4015
- } finally {
4016
4268
  setOtpSubmitting(false);
4017
4269
  }
4018
4270
  }, [
@@ -4020,22 +4272,25 @@ var BuyWithApplePay = React5.forwardRef(
4020
4272
  clientSecret,
4021
4273
  phoneCode,
4022
4274
  publishableKey,
4023
- persistContact,
4275
+ storeApplePaySession,
4024
4276
  amountUsd,
4025
- otpSubmitting
4277
+ otpSubmitting,
4278
+ refetchApplePayLimits,
4279
+ getViewForLimitStatus,
4280
+ applyLimitRouting
4026
4281
  ]);
4027
4282
  const prepareSession = useCallback(
4028
4283
  async (token, amt) => {
4284
+ setErrorMessage("");
4029
4285
  if (!destinationChainType || !destinationChainId) {
4030
4286
  setErrorMessage("Deposit destination not configured. Please contact support.");
4031
4287
  return;
4032
4288
  }
4033
- const destinationNetwork = destinationChainNameFor(
4034
- destinationChainType,
4035
- destinationChainId
4036
- );
4037
- if (!destinationNetwork) {
4038
- setErrorMessage("Coinbase Apple Pay is not available for this network.");
4289
+ if (defaultTokenLoading) {
4290
+ return;
4291
+ }
4292
+ if (defaultTokenError || !defaultToken) {
4293
+ setErrorMessage("Unable to load token information. Please try again.");
4039
4294
  return;
4040
4295
  }
4041
4296
  const settlementAddress = depositWallet?.address;
@@ -4046,19 +4301,17 @@ var BuyWithApplePay = React5.forwardRef(
4046
4301
  sessionAbortRef.current?.abort();
4047
4302
  const controller = new AbortController();
4048
4303
  sessionAbortRef.current = controller;
4049
- setPreparingSession(true);
4050
- setErrorMessage("");
4051
4304
  try {
4052
4305
  const externalId = generatePrefixedKSUID3("orsext");
4053
4306
  const session = await createCoinbaseApplePaySession(
4054
4307
  {
4055
4308
  source_currency: "usd",
4056
4309
  source_amount: amt,
4057
- destination_currency: destinationTokenSymbol.toLowerCase(),
4058
- destination_network: destinationNetwork,
4310
+ destination_currency: defaultToken.destination_currency,
4311
+ destination_network: defaultToken.destination_network,
4059
4312
  wallet_address: settlementAddress,
4060
4313
  external_id: externalId,
4061
- ...externalUserEmail ? { email: externalUserEmail } : {}
4314
+ ...userEmail ? { email: userEmail } : {}
4062
4315
  },
4063
4316
  token,
4064
4317
  publishableKey,
@@ -4102,18 +4355,16 @@ var BuyWithApplePay = React5.forwardRef(
4102
4355
  setPaymentSession(null);
4103
4356
  setPaymentSessionAmount(null);
4104
4357
  setErrorMessage("Unable to process your request. Please try again later.");
4105
- } finally {
4106
- if (sessionAbortRef.current === controller) {
4107
- setPreparingSession(false);
4108
- }
4109
4358
  }
4110
4359
  },
4111
4360
  [
4112
- destinationTokenSymbol,
4113
4361
  destinationChainType,
4114
4362
  destinationChainId,
4363
+ defaultToken,
4364
+ defaultTokenLoading,
4365
+ defaultTokenError,
4115
4366
  depositWallet?.address,
4116
- externalUserEmail,
4367
+ userEmail,
4117
4368
  publishableKey,
4118
4369
  onEvent,
4119
4370
  userId
@@ -4157,42 +4408,8 @@ var BuyWithApplePay = React5.forwardRef(
4157
4408
  if (view === "loading_config") {
4158
4409
  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
4410
  }
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
4411
  if (view === "email_input") {
4195
- const emailValid = EMAIL_REGEX.test(email);
4412
+ const emailValid = EMAIL_REGEX2.test(email);
4196
4413
  return /* @__PURE__ */ jsxs11("div", { className: "uf-flex uf-flex-col uf-pb-1 uf-pt-2", style: containerStyle, children: [
4197
4414
  /* @__PURE__ */ jsx13(
4198
4415
  "label",
@@ -4209,9 +4426,7 @@ var BuyWithApplePay = React5.forwardRef(
4209
4426
  value: email,
4210
4427
  onChange: (e) => setEmail(e.target.value.trim()),
4211
4428
  onKeyDown: (e) => {
4212
- if (e.key === "Enter" && emailValid) {
4213
- setView("phone_input");
4214
- }
4429
+ if (e.key === "Enter" && emailValid) setView("phone_input");
4215
4430
  },
4216
4431
  placeholder: "you@example.com",
4217
4432
  autoComplete: "email",
@@ -4247,7 +4462,7 @@ var BuyWithApplePay = React5.forwardRef(
4247
4462
  }
4248
4463
  if (view === "phone_input" || view === "submitting_session") {
4249
4464
  const submitting = view === "submitting_session";
4250
- const phoneValid = PHONE_REGEX.test(normalizedPhone);
4465
+ const phoneValid = PHONE_REGEX2.test(normalizedPhone);
4251
4466
  const rawDigits = (() => {
4252
4467
  const digits = phoneInput.replace(/\D/g, "");
4253
4468
  return digits.startsWith("1") ? digits.slice(1, 11) : digits.slice(0, 10);
@@ -4328,15 +4543,7 @@ var BuyWithApplePay = React5.forwardRef(
4328
4543
  !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
4544
  errorMessage && /* @__PURE__ */ jsx13("div", { className: "uf-text-xs uf-mt-2 uf-px-1", style: { color: colors2.error }, children: errorMessage }),
4330
4545
  /* @__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
- ),
4546
+ /* @__PURE__ */ jsx13(LegalDisclaimer, { legalAgreements, loading: legalAgreementsLoading }),
4340
4547
  /* @__PURE__ */ jsx13(
4341
4548
  "button",
4342
4549
  {
@@ -4355,12 +4562,9 @@ var BuyWithApplePay = React5.forwardRef(
4355
4562
  )
4356
4563
  ] });
4357
4564
  }
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;
4565
+ if (view === "phone_otp") {
4566
+ const code = phoneCode;
4567
+ const target = maskPhone(normalizedPhone);
4364
4568
  const canSubmit = code.length === 6 && !otpSubmitting;
4365
4569
  return /* @__PURE__ */ jsxs11("div", { className: "uf-flex uf-flex-col uf-pb-1 uf-items-center uf-pt-2", style: containerStyle, children: [
4366
4570
  /* @__PURE__ */ jsxs11(
@@ -4383,13 +4587,12 @@ var BuyWithApplePay = React5.forwardRef(
4383
4587
  maxLength: 6,
4384
4588
  value: code,
4385
4589
  onChange: (e) => {
4386
- const v = e.target.value.replace(/\D/g, "").slice(0, 6);
4387
- setCode(v);
4590
+ setPhoneCode(e.target.value.replace(/\D/g, "").slice(0, 6));
4388
4591
  setOtpError(null);
4389
4592
  },
4390
4593
  onKeyDown: (e) => {
4391
4594
  if (e.key === "Enter" && code.length === 6 && !otpSubmitting) {
4392
- void submit();
4595
+ void submitPhoneOtp();
4393
4596
  }
4394
4597
  },
4395
4598
  autoFocus: true,
@@ -4416,7 +4619,7 @@ var BuyWithApplePay = React5.forwardRef(
4416
4619
  if (!verificationSession || !clientSecret) return;
4417
4620
  void sendOnrampVerificationOtp(
4418
4621
  verificationSession.id,
4419
- isEmail ? "email" : "phone",
4622
+ "phone",
4420
4623
  clientSecret,
4421
4624
  publishableKey
4422
4625
  ).then(() => setResendCooldown(30));
@@ -4429,7 +4632,7 @@ var BuyWithApplePay = React5.forwardRef(
4429
4632
  /* @__PURE__ */ jsx13(
4430
4633
  "button",
4431
4634
  {
4432
- onClick: () => void submit(),
4635
+ onClick: submitPhoneOtp,
4433
4636
  disabled: !canSubmit,
4434
4637
  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
4638
  style: {
@@ -4577,7 +4780,7 @@ var BuyWithApplePay = React5.forwardRef(
4577
4780
  border: `${components.button.borderWidth}px solid ${components.button.borderColor}`,
4578
4781
  opacity: 0.5
4579
4782
  },
4580
- children: !hasInput || !validAmount ? "Enter amount" : /* @__PURE__ */ jsxs11(Fragment4, { children: [
4783
+ 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
4784
  /* @__PURE__ */ jsx13(Loader22, { className: "uf-w-4 uf-h-4 uf-animate-spin" }),
4582
4785
  "Preparing Apple Pay"
4583
4786
  ] })
@@ -4630,6 +4833,274 @@ var BuyWithApplePay = React5.forwardRef(
4630
4833
  }
4631
4834
  );
4632
4835
  }
4836
+ if (view === "limit_upgrade_intro") {
4837
+ return /* @__PURE__ */ jsxs11("div", { className: "uf-flex uf-flex-col uf-pt-8 uf-pb-2 uf-px-2", style: containerStyle, children: [
4838
+ /* @__PURE__ */ jsx13(
4839
+ "h3",
4840
+ {
4841
+ className: "uf-text-xl uf-mb-3 uf-text-center",
4842
+ style: { color: colors2.foreground, fontFamily: fonts.medium },
4843
+ children: "Apple Pay limit reached"
4844
+ }
4845
+ ),
4846
+ /* @__PURE__ */ jsx13(
4847
+ "p",
4848
+ {
4849
+ className: "uf-text-sm uf-text-center uf-mb-10 uf-max-w-[320px] uf-mx-auto",
4850
+ style: { color: colors2.foregroundMuted, fontFamily: fonts.regular },
4851
+ children: "Submit your date of birth and SSN to increase your Apple Pay limit."
4852
+ }
4853
+ ),
4854
+ /* @__PURE__ */ jsx13(
4855
+ "button",
4856
+ {
4857
+ onClick: () => {
4858
+ setLimitUpgradeError(null);
4859
+ setView("limit_upgrade_form");
4860
+ },
4861
+ className: "uf-w-full uf-py-3 uf-mb-2 uf-text-sm uf-font-medium uf-transition-colors",
4862
+ style: {
4863
+ backgroundColor: colors2.primary,
4864
+ color: colors2.primaryForeground,
4865
+ fontFamily: fonts.medium,
4866
+ borderRadius: components.button.borderRadius,
4867
+ border: `${components.button.borderWidth}px solid ${components.button.borderColor}`
4868
+ },
4869
+ children: "Continue"
4870
+ }
4871
+ ),
4872
+ /* @__PURE__ */ jsx13(
4873
+ "button",
4874
+ {
4875
+ onClick: () => {
4876
+ setLimitUpgradeError(null);
4877
+ onExit?.();
4878
+ },
4879
+ className: "uf-w-full uf-py-3 uf-text-sm uf-font-medium uf-transition-colors",
4880
+ style: {
4881
+ backgroundColor: "transparent",
4882
+ color: colors2.error,
4883
+ fontFamily: fonts.medium,
4884
+ borderRadius: components.button.borderRadius,
4885
+ border: `${components.button.borderWidth}px solid transparent`
4886
+ },
4887
+ children: "Cancel"
4888
+ }
4889
+ )
4890
+ ] });
4891
+ }
4892
+ if (view === "limit_upgrade_submitting") {
4893
+ return /* @__PURE__ */ jsxs11(
4894
+ "div",
4895
+ {
4896
+ className: "uf-flex uf-flex-col uf-items-center uf-justify-center uf-py-16 uf-px-4",
4897
+ style: containerStyle,
4898
+ children: [
4899
+ /* @__PURE__ */ jsx13(
4900
+ "div",
4901
+ {
4902
+ className: "uf-w-20 uf-h-20 uf-rounded-full uf-flex uf-items-center uf-justify-center uf-mb-6",
4903
+ style: { backgroundColor: `${colors2.primary}20` },
4904
+ children: /* @__PURE__ */ jsx13(
4905
+ Loader22,
4906
+ {
4907
+ className: "uf-w-10 uf-h-10 uf-animate-spin",
4908
+ style: { color: colors2.primary }
4909
+ }
4910
+ )
4911
+ }
4912
+ ),
4913
+ /* @__PURE__ */ jsx13(
4914
+ "h3",
4915
+ {
4916
+ className: "uf-text-xl uf-mb-2 uf-text-center",
4917
+ style: { color: colors2.foreground, fontFamily: fonts.medium },
4918
+ children: "Verifying your information"
4919
+ }
4920
+ ),
4921
+ /* @__PURE__ */ jsx13(
4922
+ "p",
4923
+ {
4924
+ className: "uf-text-sm uf-text-center uf-max-w-[280px]",
4925
+ style: { color: colors2.foregroundMuted, fontFamily: fonts.regular },
4926
+ children: "Your limit increase request is under review. This usually takes just a few seconds."
4927
+ }
4928
+ )
4929
+ ]
4930
+ }
4931
+ );
4932
+ }
4933
+ if (view === "limit_upgrade_form") {
4934
+ const dobValid = isDobInputValid(dobInput);
4935
+ const ssnValid = /^\d{4}$/.test(ssnLast4);
4936
+ const formValid = dobValid && ssnValid;
4937
+ const formInputStyle = {
4938
+ backgroundColor: components.card.backgroundColor,
4939
+ color: components.input.textColor,
4940
+ fontFamily: fonts.regular,
4941
+ borderRadius: components.card.borderRadius,
4942
+ border: `${components.card.borderWidth}px solid ${components.card.borderColor}`
4943
+ };
4944
+ return /* @__PURE__ */ jsxs11("div", { className: "uf-flex uf-flex-col uf-pb-1 uf-pt-2", style: containerStyle, children: [
4945
+ /* @__PURE__ */ jsx13(
4946
+ "label",
4947
+ {
4948
+ className: "uf-text-xs uf-font-medium uf-mb-1.5 uf-px-1",
4949
+ style: { color: components.card.labelColor, fontFamily: fonts.medium },
4950
+ children: "Date of birth"
4951
+ }
4952
+ ),
4953
+ /* @__PURE__ */ jsx13(
4954
+ "input",
4955
+ {
4956
+ type: "text",
4957
+ inputMode: "numeric",
4958
+ maxLength: 10,
4959
+ value: dobInput,
4960
+ onChange: (e) => {
4961
+ setDobInput(formatDobInput(e.target.value));
4962
+ if (limitUpgradeError) setLimitUpgradeError(null);
4963
+ },
4964
+ onKeyDown: (e) => {
4965
+ if (e.key === "Enter" && formValid) void submitLimitUpgrade();
4966
+ },
4967
+ placeholder: "MM/DD/YYYY",
4968
+ autoComplete: "bday",
4969
+ "aria-label": "Date of birth",
4970
+ className: "uf-w-full uf-px-3 uf-py-3 uf-mb-3 uf-text-sm uf-outline-none",
4971
+ style: formInputStyle
4972
+ }
4973
+ ),
4974
+ /* @__PURE__ */ jsx13(
4975
+ "label",
4976
+ {
4977
+ className: "uf-text-xs uf-font-medium uf-mb-1.5 uf-px-1",
4978
+ style: { color: components.card.labelColor, fontFamily: fonts.medium },
4979
+ children: "Last 4 of SSN"
4980
+ }
4981
+ ),
4982
+ /* @__PURE__ */ jsx13(
4983
+ "input",
4984
+ {
4985
+ type: "password",
4986
+ inputMode: "numeric",
4987
+ pattern: "[0-9]*",
4988
+ maxLength: 4,
4989
+ value: ssnLast4,
4990
+ onChange: (e) => {
4991
+ setSsnLast4(e.target.value.replace(/\D/g, "").slice(0, 4));
4992
+ if (limitUpgradeError) setLimitUpgradeError(null);
4993
+ },
4994
+ onKeyDown: (e) => {
4995
+ if (e.key === "Enter" && formValid) void submitLimitUpgrade();
4996
+ },
4997
+ placeholder: "\u2022\u2022\u2022\u2022",
4998
+ autoComplete: "off",
4999
+ name: "off",
5000
+ autoCorrect: "off",
5001
+ autoCapitalize: "none",
5002
+ spellCheck: false,
5003
+ "data-1p-ignore": "true",
5004
+ "data-lpignore": "true",
5005
+ "data-form-type": "other",
5006
+ "aria-label": "Last 4 digits of SSN",
5007
+ "aria-autocomplete": "none",
5008
+ className: "uf-w-full uf-px-3 uf-py-3 uf-mb-2 uf-text-sm uf-outline-none",
5009
+ style: formInputStyle
5010
+ }
5011
+ ),
5012
+ limitUpgradeError && /* @__PURE__ */ jsx13(
5013
+ "div",
5014
+ {
5015
+ className: "uf-text-xs uf-mt-2 uf-px-1",
5016
+ style: { color: colors2.error, fontFamily: fonts.regular },
5017
+ children: limitUpgradeError
5018
+ }
5019
+ ),
5020
+ /* @__PURE__ */ jsx13("div", { className: "uf-flex-1" }),
5021
+ /* @__PURE__ */ jsx13(
5022
+ "button",
5023
+ {
5024
+ onClick: () => void submitLimitUpgrade(),
5025
+ disabled: !formValid || upgradeSubmitting,
5026
+ 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",
5027
+ style: {
5028
+ backgroundColor: colors2.primary,
5029
+ color: colors2.primaryForeground,
5030
+ fontFamily: fonts.medium,
5031
+ borderRadius: components.button.borderRadius,
5032
+ border: `${components.button.borderWidth}px solid ${components.button.borderColor}`
5033
+ },
5034
+ children: upgradeSubmitting ? /* @__PURE__ */ jsxs11("span", { className: "uf-flex uf-items-center uf-justify-center uf-gap-2", children: [
5035
+ /* @__PURE__ */ jsx13(Loader22, { className: "uf-w-4 uf-h-4 uf-animate-spin" }),
5036
+ "Submitting\u2026"
5037
+ ] }) : "Submit"
5038
+ }
5039
+ )
5040
+ ] });
5041
+ }
5042
+ if (view === "limit_upgrade_failed") {
5043
+ return /* @__PURE__ */ jsxs11("div", { className: "uf-flex uf-flex-col uf-pt-8 uf-pb-2 uf-px-2", style: containerStyle, children: [
5044
+ /* @__PURE__ */ jsx13(
5045
+ "h3",
5046
+ {
5047
+ className: "uf-text-xl uf-mb-3 uf-text-center",
5048
+ style: { color: colors2.foreground, fontFamily: fonts.medium },
5049
+ children: "Apple Pay limit reached"
5050
+ }
5051
+ ),
5052
+ /* @__PURE__ */ jsxs11(
5053
+ "p",
5054
+ {
5055
+ className: "uf-text-sm uf-text-center uf-mb-2 uf-max-w-[320px] uf-mx-auto",
5056
+ style: { color: colors2.foregroundMuted, fontFamily: fonts.regular },
5057
+ children: [
5058
+ "Failed to increase. Please try again later or",
5059
+ " ",
5060
+ /* @__PURE__ */ jsx13(
5061
+ "a",
5062
+ {
5063
+ href: UNIFOLD_SUPPORT_URL,
5064
+ target: "_blank",
5065
+ rel: "noreferrer noopener",
5066
+ className: "uf-underline",
5067
+ style: { color: colors2.primary },
5068
+ children: "contact support"
5069
+ }
5070
+ ),
5071
+ "."
5072
+ ]
5073
+ }
5074
+ ),
5075
+ limitUpgradeError && /* @__PURE__ */ jsx13(
5076
+ "p",
5077
+ {
5078
+ className: "uf-text-xs uf-text-center uf-mb-2 uf-px-2",
5079
+ style: { color: colors2.error, fontFamily: fonts.regular },
5080
+ children: limitUpgradeError
5081
+ }
5082
+ ),
5083
+ /* @__PURE__ */ jsx13("div", { className: "uf-mb-8" }),
5084
+ /* @__PURE__ */ jsx13(
5085
+ "button",
5086
+ {
5087
+ onClick: () => {
5088
+ setLimitUpgradeError(null);
5089
+ onExit?.();
5090
+ },
5091
+ className: "uf-w-full uf-py-3 uf-text-sm uf-font-medium uf-transition-colors",
5092
+ style: {
5093
+ backgroundColor: colors2.primary,
5094
+ color: colors2.primaryForeground,
5095
+ fontFamily: fonts.medium,
5096
+ borderRadius: components.button.borderRadius,
5097
+ border: `${components.button.borderWidth}px solid ${components.button.borderColor}`
5098
+ },
5099
+ children: "Return"
5100
+ }
5101
+ )
5102
+ ] });
5103
+ }
4633
5104
  if (view === "guest_limit_reached") {
4634
5105
  return /* @__PURE__ */ jsxs11(
4635
5106
  "div",
@@ -4650,7 +5121,7 @@ var BuyWithApplePay = React5.forwardRef(
4650
5121
  {
4651
5122
  className: "uf-text-sm uf-text-center uf-mb-12 uf-max-w-[300px]",
4652
5123
  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."
5124
+ children: "Your account is not eligible for a limit increase at this time."
4654
5125
  }
4655
5126
  ),
4656
5127
  /* @__PURE__ */ jsx13(
@@ -4676,49 +5147,18 @@ var BuyWithApplePay = React5.forwardRef(
4676
5147
  }
4677
5148
  );
4678
5149
  BuyWithApplePay.displayName = "BuyWithApplePay";
4679
- function LegalDisclaimer({ legalAgreements, loading, colors: colors2, fonts }) {
4680
- if (loading) {
4681
- return /* @__PURE__ */ jsxs11(
4682
- "div",
4683
- {
4684
- className: "uf-mt-3 uf-mb-3 uf-px-1 uf-space-y-1.5 uf-animate-pulse",
4685
- "aria-hidden": true,
4686
- "aria-busy": "true",
4687
- children: [
4688
- /* @__PURE__ */ jsx13("div", { className: "uf-h-2.5 uf-rounded uf-w-full", style: { backgroundColor: colors2.card } }),
4689
- /* @__PURE__ */ jsx13("div", { className: "uf-h-2.5 uf-rounded uf-w-11/12", style: { backgroundColor: colors2.card } }),
4690
- /* @__PURE__ */ jsx13("div", { className: "uf-h-2.5 uf-rounded uf-w-3/4", style: { backgroundColor: colors2.card } })
4691
- ]
4692
- }
4693
- );
5150
+ function formatPhoneInput(raw) {
5151
+ const digits = raw.replace(/\D/g, "");
5152
+ if (raw.startsWith("+")) {
5153
+ return `+${digits}`;
4694
5154
  }
4695
- const agreements = legalAgreements?.agreements ?? FALLBACK_AGREEMENTS;
4696
- return /* @__PURE__ */ jsxs11(
4697
- "p",
4698
- {
4699
- className: "uf-text-[11px] uf-leading-relaxed uf-mt-3 uf-mb-3 uf-px-1",
4700
- style: { color: colors2.foregroundMuted, fontFamily: fonts.regular },
4701
- children: [
4702
- "By continuing, you agree to Coinbase's",
4703
- " ",
4704
- agreements.map((a, idx, arr) => /* @__PURE__ */ jsxs11(React5.Fragment, { children: [
4705
- /* @__PURE__ */ jsx13(
4706
- "a",
4707
- {
4708
- href: a.url,
4709
- target: "_blank",
4710
- rel: "noopener noreferrer",
4711
- className: "uf-underline",
4712
- style: { color: colors2.foreground },
4713
- children: a.name
4714
- }
4715
- ),
4716
- idx < arr.length - 2 ? ", " : idx === arr.length - 2 ? ", and " : ""
4717
- ] }, a.key)),
4718
- ". Coinbase is the regulated provider of this fiat-to-crypto onramp."
4719
- ]
4720
- }
4721
- );
5155
+ if (digits.length === 10) return `+1${digits}`;
5156
+ if (digits.length === 11 && digits.startsWith("1")) return `+${digits}`;
5157
+ return digits.length > 0 ? `+${digits}` : "";
5158
+ }
5159
+ function maskPhone(phone) {
5160
+ if (!/^\+1\d{10}$/.test(phone)) return phone;
5161
+ return `+1 (***) ***-${phone.slice(-4)}`;
4722
5162
  }
4723
5163
  function isGuestLimitError(err) {
4724
5164
  const message = err instanceof Error ? err.message : String(err ?? "");
@@ -4726,6 +5166,12 @@ function isGuestLimitError(err) {
4726
5166
  if (/guest_(daily_)?transaction_(count|limit)/i.test(message)) return true;
4727
5167
  return false;
4728
5168
  }
5169
+ function isLimitUpgradeTerminalBlock(message) {
5170
+ if (/coinbase_onramp_guest_limit_reached/i.test(message)) return true;
5171
+ if (/limit upgrade is not available/i.test(message)) return true;
5172
+ if (/limits.?upgrade.?blocked/i.test(message)) return true;
5173
+ return false;
5174
+ }
4729
5175
  function prettifyVerificationError(message) {
4730
5176
  if (/\b429\b|too many requests|rate.?limit|throttle/i.test(message)) {
4731
5177
  return "Too many attempts. Please wait a few minutes and try again.";
@@ -4736,7 +5182,40 @@ function prettifyVerificationError(message) {
4736
5182
  if (/voip|not a valid US|not a cell/i.test(message)) {
4737
5183
  return "We can only verify real US cell phones (not VoIP). Try a different number.";
4738
5184
  }
4739
- return message;
5185
+ const cleaned = message.replace(/\s*\[[\w_]+\]:\s*.*$/, "").trim();
5186
+ return cleaned || message;
5187
+ }
5188
+ function formatDobInput(raw) {
5189
+ const digits = raw.replace(/\D/g, "").slice(0, 8);
5190
+ if (digits.length <= 2) return digits;
5191
+ if (digits.length <= 4) return `${digits.slice(0, 2)}/${digits.slice(2)}`;
5192
+ return `${digits.slice(0, 2)}/${digits.slice(2, 4)}/${digits.slice(4)}`;
5193
+ }
5194
+ function isDobInputValid(input) {
5195
+ return parseDobInput(input) !== null;
5196
+ }
5197
+ function parseDobInput(input) {
5198
+ const m = /^(\d{2})\/(\d{2})\/(\d{4})$/.exec(input);
5199
+ if (!m) return null;
5200
+ const [, month, day, year] = m;
5201
+ if (!/^(0[1-9]|1[0-2])$/.test(month)) return null;
5202
+ if (!/^(0[1-9]|[12]\d|3[01])$/.test(day)) return null;
5203
+ if (!/^(19|20)\d{2}$/.test(year)) return null;
5204
+ return { month, day, year };
5205
+ }
5206
+ var POPUP_W = 500;
5207
+ var POPUP_H = 700;
5208
+ function openCoinbasePaymentPopup(url) {
5209
+ if (typeof window === "undefined") return null;
5210
+ const screenW = window.screen.availWidth ?? window.innerWidth;
5211
+ const screenH = window.screen.availHeight ?? window.innerHeight;
5212
+ const left = Math.max(0, Math.round((screenW - POPUP_W) / 2));
5213
+ const top = Math.max(0, Math.round((screenH - POPUP_H) / 2));
5214
+ return window.open(
5215
+ url,
5216
+ "unifold_coinbase_apple_pay",
5217
+ `popup,width=${POPUP_W},height=${POPUP_H},left=${left},top=${top}`
5218
+ );
4740
5219
  }
4741
5220
  var FALLBACK_AGREEMENTS = [
4742
5221
  {
@@ -4755,42 +5234,50 @@ var FALLBACK_AGREEMENTS = [
4755
5234
  url: "https://www.coinbase.com/legal/privacy"
4756
5235
  }
4757
5236
  ];
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;
5237
+ function LegalDisclaimer({ legalAgreements, loading }) {
5238
+ const { colors: colors2, fonts } = useTheme();
5239
+ if (loading) {
5240
+ return /* @__PURE__ */ jsxs11(
5241
+ "div",
5242
+ {
5243
+ className: "uf-mt-3 uf-mb-3 uf-px-1 uf-space-y-1.5 uf-animate-pulse",
5244
+ "aria-hidden": true,
5245
+ "aria-busy": "true",
5246
+ children: [
5247
+ /* @__PURE__ */ jsx13("div", { className: "uf-h-2.5 uf-rounded uf-w-full", style: { backgroundColor: colors2.card } }),
5248
+ /* @__PURE__ */ jsx13("div", { className: "uf-h-2.5 uf-rounded uf-w-11/12", style: { backgroundColor: colors2.card } }),
5249
+ /* @__PURE__ */ jsx13("div", { className: "uf-h-2.5 uf-rounded uf-w-3/4", style: { backgroundColor: colors2.card } })
5250
+ ]
5251
+ }
5252
+ );
5253
+ }
5254
+ const agreements = legalAgreements?.agreements ?? FALLBACK_AGREEMENTS;
5255
+ return /* @__PURE__ */ jsxs11(
5256
+ "p",
5257
+ {
5258
+ className: "uf-text-[11px] uf-leading-relaxed uf-mt-3 uf-mb-3 uf-px-1",
5259
+ style: { color: colors2.foregroundMuted, fontFamily: fonts.regular },
5260
+ children: [
5261
+ "By continuing, you agree to Coinbase's",
5262
+ " ",
5263
+ agreements.map((a, idx, arr) => /* @__PURE__ */ jsxs11(React5.Fragment, { children: [
5264
+ /* @__PURE__ */ jsx13(
5265
+ "a",
5266
+ {
5267
+ href: a.url,
5268
+ target: "_blank",
5269
+ rel: "noopener noreferrer",
5270
+ className: "uf-underline",
5271
+ style: { color: colors2.foreground },
5272
+ children: a.name
5273
+ }
5274
+ ),
5275
+ idx < arr.length - 2 ? ", " : idx === arr.length - 2 ? ", and " : ""
5276
+ ] }, a.key)),
5277
+ ". Coinbase is the regulated provider of this fiat-to-crypto onramp."
5278
+ ]
4791
5279
  }
4792
- }
4793
- return null;
5280
+ );
4794
5281
  }
4795
5282
 
4796
5283
  // src/components/deposits/PayWithExchange.tsx
@@ -5059,7 +5546,7 @@ import {
5059
5546
  } from "@unifold/core";
5060
5547
 
5061
5548
  // src/components/deposits/StyledQRCode.tsx
5062
- import { useEffect as useEffect8, useRef as useRef4, useState as useState13, useMemo as useMemo3 } from "react";
5549
+ import { useEffect as useEffect8, useRef as useRef4, useState as useState13, useMemo as useMemo4 } from "react";
5063
5550
  import QRCodeStyling from "qr-code-styling";
5064
5551
  import { jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
5065
5552
  function createQRConfig(value, size, imageUrl, imageSize, darkMode) {
@@ -5112,7 +5599,7 @@ function QRCodeSkeleton({ size = 180, darkMode = false }) {
5112
5599
  const spacing = size / gridCount;
5113
5600
  const fillColor = darkMode ? "rgba(255,255,255,0.10)" : "rgba(0,0,0,0.08)";
5114
5601
  const cornerColor = darkMode ? "rgba(255,255,255,0.14)" : "rgba(0,0,0,0.12)";
5115
- const dots = useMemo3(() => {
5602
+ const dots = useMemo4(() => {
5116
5603
  const result = [];
5117
5604
  const cornerSize = 7;
5118
5605
  const centerStart = Math.floor(gridCount / 2) - 2;
@@ -5216,10 +5703,10 @@ function useIsMobileViewport() {
5216
5703
  }
5217
5704
 
5218
5705
  // src/hooks/use-cashapp-limits.ts
5219
- import { useQuery as useQuery5 } from "@tanstack/react-query";
5706
+ import { useQuery as useQuery6 } from "@tanstack/react-query";
5220
5707
  import { getCashAppLimits } from "@unifold/core";
5221
5708
  function useCashAppLimits({ publishableKey, currency = "usd" }) {
5222
- return useQuery5({
5709
+ return useQuery6({
5223
5710
  queryKey: ["unifold", "cashAppLimits", currency, publishableKey],
5224
5711
  queryFn: () => getCashAppLimits(currency, publishableKey),
5225
5712
  enabled: !!publishableKey,
@@ -5737,7 +6224,7 @@ function PayWithCashApp({
5737
6224
  }
5738
6225
 
5739
6226
  // src/components/deposits/BankTransfer.tsx
5740
- import { useEffect as useEffect11, useMemo as useMemo4, useState as useState16 } from "react";
6227
+ import { useEffect as useEffect11, useMemo as useMemo5, useState as useState16 } from "react";
5741
6228
  import { ChevronRight as ChevronRight3, ExternalLink as ExternalLink3, Landmark } from "lucide-react";
5742
6229
  import {
5743
6230
  DepositEventType as DepositEventType5,
@@ -5747,47 +6234,6 @@ import {
5747
6234
  generatePrefixedKSUID as generatePrefixedKSUID5
5748
6235
  } from "@unifold/core";
5749
6236
 
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
6237
  // src/hooks/use-bank-transfer-providers.ts
5792
6238
  import { useQuery as useQuery7 } from "@tanstack/react-query";
5793
6239
  import { getBankTransferProviders } from "@unifold/core";
@@ -5872,7 +6318,7 @@ function BankTransfer({
5872
6318
  countryCode: userIpInfo?.alpha2
5873
6319
  });
5874
6320
  const providers = providersResponse?.data ?? [];
5875
- const pollingWalletId = useMemo4(() => {
6321
+ const pollingWalletId = useMemo5(() => {
5876
6322
  if (!defaultToken) return void 0;
5877
6323
  return getWalletByChainType3(
5878
6324
  wallets,
@@ -5893,7 +6339,7 @@ function BankTransfer({
5893
6339
  const currencySymbol = getCurrencySymbol2(sourceCurrency);
5894
6340
  const parsedAmount = parseFloat(amount);
5895
6341
  const amountValid = !!amount && Number.isFinite(parsedAmount) && parsedAmount >= MIN_AMOUNT;
5896
- const displayTokenSymbol = useMemo4(
6342
+ const displayTokenSymbol = useMemo5(
5897
6343
  () => destinationTokenSymbol?.toUpperCase() ?? defaultToken?.destination_token_metadata?.symbol?.toUpperCase() ?? defaultToken?.destination_currency?.toUpperCase() ?? "USDC",
5898
6344
  [destinationTokenSymbol, defaultToken]
5899
6345
  );
@@ -10013,16 +10459,114 @@ function AmexIcon({ size = 24 }) {
10013
10459
  )
10014
10460
  ] });
10015
10461
  }
10462
+ function ApplePayIcon({ size = 24, color = "#000" }) {
10463
+ const h = size * 0.62;
10464
+ const w = h * (512 / 210.2);
10465
+ return /* @__PURE__ */ jsx43("svg", { width: w, height: h, viewBox: "0 0 512 210.2", fill: "none", children: /* @__PURE__ */ jsx43(
10466
+ "path",
10467
+ {
10468
+ fill: color,
10469
+ 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"
10470
+ }
10471
+ ) });
10472
+ }
10473
+ function GooglePayIcon({ size = 24, color = "#202124" }) {
10474
+ const h = size * 0.62;
10475
+ const w = h * (80 / 38.1);
10476
+ return /* @__PURE__ */ jsxs40("svg", { width: w, height: h, viewBox: "0 0 80 38.1", fill: "none", children: [
10477
+ /* @__PURE__ */ jsx43(
10478
+ "path",
10479
+ {
10480
+ fill: color,
10481
+ 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"
10482
+ }
10483
+ ),
10484
+ /* @__PURE__ */ jsx43(
10485
+ "path",
10486
+ {
10487
+ fill: color,
10488
+ 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"
10489
+ }
10490
+ ),
10491
+ /* @__PURE__ */ jsx43(
10492
+ "path",
10493
+ {
10494
+ fill: color,
10495
+ d: "M80,13.3l-9.9,22.7h-3l3.7-7.9l-6.5-14.7h3.2l4.7,11.3h0.1l4.6-11.3H80z"
10496
+ }
10497
+ ),
10498
+ /* @__PURE__ */ jsx43(
10499
+ "path",
10500
+ {
10501
+ fill: "#4285F4",
10502
+ 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"
10503
+ }
10504
+ ),
10505
+ /* @__PURE__ */ jsx43(
10506
+ "path",
10507
+ {
10508
+ fill: "#34A853",
10509
+ 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"
10510
+ }
10511
+ ),
10512
+ /* @__PURE__ */ jsx43(
10513
+ "path",
10514
+ {
10515
+ fill: "#FBBC04",
10516
+ 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"
10517
+ }
10518
+ ),
10519
+ /* @__PURE__ */ jsx43(
10520
+ "path",
10521
+ {
10522
+ fill: "#EA4335",
10523
+ 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"
10524
+ }
10525
+ )
10526
+ ] });
10527
+ }
10016
10528
  function GenericCardIcon({ size = 24, color = "#888" }) {
10017
10529
  return /* @__PURE__ */ jsx43(CreditCard2, { width: size * 0.8, height: size * 0.8, style: { color }, strokeWidth: 1.5 });
10018
10530
  }
10531
+ function parseColor(input) {
10532
+ if (!input) return null;
10533
+ const s = input.trim();
10534
+ if (s.startsWith("#")) {
10535
+ let hex = s.slice(1);
10536
+ if (hex.length === 3)
10537
+ hex = hex.split("").map((c) => c + c).join("");
10538
+ if (hex.length !== 6) return null;
10539
+ const n = parseInt(hex, 16);
10540
+ if (Number.isNaN(n)) return null;
10541
+ return [n >> 16 & 255, n >> 8 & 255, n & 255];
10542
+ }
10543
+ const m = s.match(/rgba?\(([^)]+)\)/i);
10544
+ if (m) {
10545
+ const parts = m[1].split(",").map((p) => parseFloat(p));
10546
+ if (parts.length >= 3 && parts.every((p) => !Number.isNaN(p)))
10547
+ return [parts[0], parts[1], parts[2]];
10548
+ }
10549
+ return null;
10550
+ }
10551
+ function monochromeOn(background) {
10552
+ const rgb = parseColor(background);
10553
+ if (!rgb) return "#000000";
10554
+ const [r, g, b] = rgb;
10555
+ const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255;
10556
+ return luminance > 0.6 ? "#000000" : "#FFFFFF";
10557
+ }
10019
10558
  function CardBrandIcon({
10020
10559
  brand,
10021
10560
  size = 24,
10022
- fallbackColor = "#888"
10561
+ fallbackColor = "#888",
10562
+ monochromeColor = "#000000"
10023
10563
  }) {
10024
10564
  if (!brand) return /* @__PURE__ */ jsx43(GenericCardIcon, { size, color: fallbackColor });
10025
10565
  const lower = brand.toLowerCase();
10566
+ if (lower.includes("apple_pay") || lower.includes("apple pay") || lower === "applepay")
10567
+ return /* @__PURE__ */ jsx43(ApplePayIcon, { size, color: monochromeColor });
10568
+ if (lower.includes("google_pay") || lower.includes("google pay") || lower === "googlepay")
10569
+ return /* @__PURE__ */ jsx43(GooglePayIcon, { size, color: monochromeColor === "#FFFFFF" ? "#FFFFFF" : "#202124" });
10026
10570
  if (lower.includes("visa")) return /* @__PURE__ */ jsx43(VisaIcon, { size });
10027
10571
  if (lower.includes("mastercard") || lower.includes("master"))
10028
10572
  return /* @__PURE__ */ jsx43(MastercardIcon, { size });
@@ -10031,17 +10575,28 @@ function CardBrandIcon({
10031
10575
  }
10032
10576
  function formatPaymentToken(token) {
10033
10577
  if (token.card) {
10034
- const brand = token.card.brand ?? "Card";
10578
+ const cardBrand = token.card.brand ?? "Card";
10579
+ const last4 = token.card.last4;
10580
+ const walletType = token.card.wallet?.type;
10581
+ if (walletType === "apple_pay" || walletType === "google_pay") {
10582
+ const funding = token.card.funding ? capitalize(token.card.funding) : null;
10583
+ return {
10584
+ label: last4 ? `${capitalize(cardBrand)} \u2022\u2022\u2022\u2022 ${last4}` : capitalize(cardBrand),
10585
+ sublabel: funding ?? "",
10586
+ brand: walletType
10587
+ };
10588
+ }
10035
10589
  return {
10036
- label: `${capitalize(brand)} \u2022\u2022\u2022\u2022 ${token.card.last4}`,
10590
+ label: `${capitalize(cardBrand)} \u2022\u2022\u2022\u2022 ${last4}`,
10037
10591
  sublabel: `${capitalize(token.card.funding ?? "card")} \xB7 Expires ${token.card.exp_month}/${token.card.exp_year}`,
10038
- brand
10592
+ brand: cardBrand
10039
10593
  };
10040
10594
  }
10041
10595
  if (token.us_bank_account) {
10596
+ const accountType = token.us_bank_account.account_type;
10042
10597
  return {
10043
10598
  label: `${token.us_bank_account.bank_name ?? "Bank"} \u2022\u2022\u2022\u2022 ${token.us_bank_account.last4}`,
10044
- sublabel: "Bank account"
10599
+ sublabel: accountType ? `${capitalize(accountType)} account` : "Bank account"
10045
10600
  };
10046
10601
  }
10047
10602
  return { label: "Payment method", sublabel: token.type };
@@ -10072,59 +10627,82 @@ import {
10072
10627
 
10073
10628
  // src/components/deposits/stripe-link/use-stripe-onramp.ts
10074
10629
  import { useState as useState28, useEffect as useEffect23, useRef as useRef7, useCallback as useCallback3 } from "react";
10075
- function useStripeOnramp(stripePublishableKey) {
10630
+ function useStripeOnramp(stripePublishableKey, isDark = false) {
10076
10631
  const [coordinator, setCoordinator] = useState28(null);
10077
10632
  const [isLoading, setIsLoading] = useState28(false);
10078
10633
  const [error, setError] = useState28(null);
10079
10634
  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.");
10635
+ const coordinatorThemeRef = useRef7(null);
10636
+ const initPromiseRef = useRef7(null);
10637
+ const isDarkRef = useRef7(isDark);
10638
+ isDarkRef.current = isDark;
10639
+ const initialize = useCallback3(() => {
10640
+ if (coordinatorRef.current) return Promise.resolve(coordinatorRef.current);
10641
+ if (initPromiseRef.current) return initPromiseRef.current;
10642
+ if (!stripePublishableKey) return Promise.resolve(null);
10643
+ const theme = isDarkRef.current;
10644
+ const promise = (async () => {
10645
+ setIsLoading(true);
10646
+ setError(null);
10647
+ try {
10648
+ console.log("[PayWithStripeLink] Importing @stripe/crypto...");
10649
+ const { loadCryptoOnrampAndInitialize } = await import(
10650
+ /* @vite-ignore */
10651
+ "@stripe/crypto"
10652
+ );
10653
+ console.log(
10654
+ "[PayWithStripeLink] @stripe/crypto imported, initializing with key:",
10655
+ stripePublishableKey?.slice(0, 12) + "..."
10656
+ );
10657
+ const initPromise = loadCryptoOnrampAndInitialize(stripePublishableKey, {
10658
+ theme: theme ? "night" : "stripe"
10659
+ });
10660
+ const timeoutPromise = new Promise(
10661
+ (_, reject) => setTimeout(
10662
+ () => reject(
10663
+ new Error(
10664
+ "Stripe SDK initialization timed out after 15 seconds. Check that your Stripe account has the Crypto Onramp feature enabled."
10665
+ )
10666
+ ),
10667
+ 15e3
10668
+ )
10669
+ );
10670
+ const instance = await Promise.race([initPromise, timeoutPromise]);
10671
+ if (!instance) {
10672
+ throw new Error("Stripe SDK returned null.");
10673
+ }
10674
+ console.log("[PayWithStripeLink] Stripe SDK initialized successfully");
10675
+ coordinatorRef.current = instance;
10676
+ coordinatorThemeRef.current = theme;
10677
+ setCoordinator(instance);
10678
+ return coordinatorRef.current;
10679
+ } catch (err) {
10680
+ const raw = err instanceof Error ? err.message : String(err);
10681
+ 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}`;
10682
+ setError(msg);
10683
+ console.error("[PayWithStripeLink]", msg);
10684
+ return null;
10685
+ } finally {
10686
+ setIsLoading(false);
10687
+ initPromiseRef.current = null;
10109
10688
  }
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
- }
10689
+ })();
10690
+ initPromiseRef.current = promise;
10691
+ return promise;
10123
10692
  }, [stripePublishableKey]);
10693
+ useEffect23(() => {
10694
+ if (!coordinatorRef.current) return;
10695
+ if (coordinatorThemeRef.current === isDark) return;
10696
+ coordinatorRef.current.destroy();
10697
+ coordinatorRef.current = null;
10698
+ coordinatorThemeRef.current = null;
10699
+ setCoordinator(null);
10700
+ }, [isDark, coordinator]);
10124
10701
  useEffect23(() => {
10125
10702
  return () => {
10126
10703
  coordinatorRef.current?.destroy();
10127
10704
  coordinatorRef.current = null;
10705
+ coordinatorThemeRef.current = null;
10128
10706
  };
10129
10707
  }, []);
10130
10708
  return { coordinator, isLoading, error, initialize };
@@ -10280,7 +10858,7 @@ function PayWithStripeLink({
10280
10858
  onDepositSuccess,
10281
10859
  onDepositError
10282
10860
  }) {
10283
- const { colors: colors2, fonts, components } = useTheme();
10861
+ const { colors: colors2, fonts, components, isDark } = useTheme();
10284
10862
  const [step, setStepInternal] = useState29(controlledStep ?? "amount");
10285
10863
  const setStep = useCallback4(
10286
10864
  (s) => {
@@ -10289,6 +10867,8 @@ function PayWithStripeLink({
10289
10867
  },
10290
10868
  [onStepChange]
10291
10869
  );
10870
+ const stepRef = useRef8(step);
10871
+ stepRef.current = step;
10292
10872
  useEffect24(() => {
10293
10873
  if (controlledStep && controlledStep !== step) {
10294
10874
  setStepInternal(controlledStep);
@@ -10356,7 +10936,7 @@ function PayWithStripeLink({
10356
10936
  isLoading: sdkLoading,
10357
10937
  error: sdkError,
10358
10938
  initialize
10359
- } = useStripeOnramp(resolvedStripeKey);
10939
+ } = useStripeOnramp(resolvedStripeKey, isDark);
10360
10940
  useEffect24(() => {
10361
10941
  if (step === "auth") {
10362
10942
  console.log("[PayWithStripeLink] Auth step state:", {
@@ -10379,6 +10959,9 @@ function PayWithStripeLink({
10379
10959
  const [errorReturnStep, setErrorReturnStep] = useState29("email");
10380
10960
  const [authIntentId, setAuthIntentId] = useState29(null);
10381
10961
  const sdkAuthenticatedRef = useRef8(false);
10962
+ const everAuthenticatedRef = useRef8(false);
10963
+ const reauthReturnStepRef = useRef8(null);
10964
+ const attemptedWalletReauthRef = useRef8(false);
10382
10965
  const pendingAddPaymentRef = useRef8(false);
10383
10966
  const autoOpenedAddPaymentRef = useRef8(false);
10384
10967
  const checkoutGenRef = useRef8(0);
@@ -10577,6 +11160,7 @@ function PayWithStripeLink({
10577
11160
  if (step === "auth" && (pendingAddPaymentRef.current || !!accessTokenRef.current)) {
10578
11161
  pendingAddPaymentRef.current = false;
10579
11162
  autoOpenedAddPaymentRef.current = false;
11163
+ reauthReturnStepRef.current = null;
10580
11164
  setError(null);
10581
11165
  setStep("amount");
10582
11166
  return true;
@@ -10594,17 +11178,34 @@ function PayWithStripeLink({
10594
11178
  );
10595
11179
  const [selectedPaymentToken, setSelectedPaymentToken] = useState29(null);
10596
11180
  const [paymentDisplay, setPaymentDisplay] = useState29(null);
11181
+ const selectedTokenSingleUseRef = useRef8(false);
10597
11182
  const selectPaymentToken = useCallback4(
10598
11183
  (token) => {
11184
+ selectedTokenSingleUseRef.current = !!token.singleUse;
10599
11185
  setSelectedPaymentToken(token.id);
10600
11186
  setPaymentDisplay(
10601
11187
  token.label ? { label: token.label, sublabel: token.sublabel, brand: token.brand } : null
10602
11188
  );
10603
11189
  setStoredSelectedTokenState(token);
10604
- if (customerId) setStoredSelectedToken(customerId, token);
11190
+ if (customerId && !token.singleUse) setStoredSelectedToken(customerId, token);
10605
11191
  },
10606
11192
  [customerId]
10607
11193
  );
11194
+ const clearSelectedPaymentToken = useCallback4(() => {
11195
+ selectedTokenSingleUseRef.current = false;
11196
+ const persisted = customerId ? getStoredSelectedToken(customerId) : null;
11197
+ if (persisted && !persisted.singleUse) {
11198
+ setSelectedPaymentToken(persisted.id);
11199
+ setPaymentDisplay(
11200
+ persisted.label ? { label: persisted.label, sublabel: persisted.sublabel, brand: persisted.brand } : null
11201
+ );
11202
+ setStoredSelectedTokenState(persisted);
11203
+ } else {
11204
+ setSelectedPaymentToken(null);
11205
+ setPaymentDisplay(null);
11206
+ setStoredSelectedTokenState(null);
11207
+ }
11208
+ }, [customerId]);
10608
11209
  const displayPaymentTokens = (() => {
10609
11210
  const apiDisplay = paymentTokens.map((t13) => ({ id: t13.id, ...formatPaymentToken(t13) }));
10610
11211
  if (storedSelectedToken && !paymentTokens.some((t13) => t13.id === storedSelectedToken.id)) {
@@ -10843,6 +11444,10 @@ function PayWithStripeLink({
10843
11444
  if (!resolvedStripeKey) return;
10844
11445
  initialize();
10845
11446
  }, [coordinator, sdkLoading, sdkError, resolvedStripeKey, initialize]);
11447
+ useEffect24(() => {
11448
+ sdkAuthenticatedRef.current = false;
11449
+ attemptedWalletReauthRef.current = false;
11450
+ }, [coordinator]);
10846
11451
  const handleAuthInterrupted = useCallback4(
10847
11452
  (message) => {
10848
11453
  const alreadySignedIn = !!accessTokenRef.current;
@@ -10856,6 +11461,26 @@ function PayWithStripeLink({
10856
11461
  },
10857
11462
  [emailProp, setStep]
10858
11463
  );
11464
+ const reauthenticateSdk = useCallback4(async () => {
11465
+ const current = stepRef.current;
11466
+ const preAuthSteps = ["email", "register", "auth"];
11467
+ reauthReturnStepRef.current = preAuthSteps.includes(current) ? null : current;
11468
+ const knownEmail = (email || emailProp || "").trim();
11469
+ if (!knownEmail) {
11470
+ reauthReturnStepRef.current = null;
11471
+ setStep("email");
11472
+ return;
11473
+ }
11474
+ try {
11475
+ const authResult = await stripeCreateAuthIntent(knownEmail, publishableKey);
11476
+ setAuthIntentId(authResult.auth_intent_id);
11477
+ setStep("auth");
11478
+ } catch (err) {
11479
+ reauthReturnStepRef.current = null;
11480
+ setError(err instanceof Error ? err.message : "Failed to re-verify. Please try again.");
11481
+ setStep("email");
11482
+ }
11483
+ }, [email, emailProp, publishableKey]);
10859
11484
  useEffect24(() => {
10860
11485
  if (step !== "auth" || !coordinator || !authIntentId) return;
10861
11486
  let mounted = true;
@@ -10866,6 +11491,7 @@ function PayWithStripeLink({
10866
11491
  if (result.result === "success" && result.crypto_customer_id) {
10867
11492
  setCustomerId(result.crypto_customer_id);
10868
11493
  sdkAuthenticatedRef.current = true;
11494
+ everAuthenticatedRef.current = true;
10869
11495
  const tokens = await stripeExchangeTokens(authIntentId, publishableKey);
10870
11496
  setOauthToken(tokens.access_token);
10871
11497
  const refresh = extractRefreshToken(tokens);
@@ -10879,16 +11505,22 @@ function PayWithStripeLink({
10879
11505
  tokens.expires_in ?? 3600,
10880
11506
  email.trim()
10881
11507
  );
11508
+ const resumeStep = reauthReturnStepRef.current;
11509
+ reauthReturnStepRef.current = null;
10882
11510
  if (pendingAddPaymentRef.current) {
10883
11511
  setStep("payment");
11512
+ } else if (resumeStep) {
11513
+ setStep(resumeStep);
10884
11514
  } else {
10885
11515
  setStep("kyc");
10886
11516
  }
10887
11517
  } else if (result.result === "abandoned") {
10888
11518
  pendingAddPaymentRef.current = false;
11519
+ reauthReturnStepRef.current = null;
10889
11520
  handleAuthInterrupted("Verification was cancelled.");
10890
11521
  } else if (result.result === "declined") {
10891
11522
  pendingAddPaymentRef.current = false;
11523
+ reauthReturnStepRef.current = null;
10892
11524
  handleAuthInterrupted("You must consent to continue.");
10893
11525
  }
10894
11526
  });
@@ -10900,6 +11532,7 @@ function PayWithStripeLink({
10900
11532
  }
10901
11533
  } catch (err) {
10902
11534
  if (!mounted) return;
11535
+ reauthReturnStepRef.current = null;
10903
11536
  const msg = err instanceof Error ? err.message : "Verification was cancelled.";
10904
11537
  handleAuthInterrupted(msg);
10905
11538
  }
@@ -11001,6 +11634,10 @@ function PayWithStripeLink({
11001
11634
  };
11002
11635
  const handleKycSubmit = async () => {
11003
11636
  if (!coordinator) return;
11637
+ if (!sdkAuthenticatedRef.current) {
11638
+ await reauthenticateSdk();
11639
+ return;
11640
+ }
11004
11641
  let kycInfo;
11005
11642
  if (kycFormMode === "address") {
11006
11643
  if (!kycForm.givenName.trim() || !kycForm.surname.trim()) {
@@ -11060,6 +11697,10 @@ function PayWithStripeLink({
11060
11697
  let mounted = true;
11061
11698
  const verify = async () => {
11062
11699
  try {
11700
+ if (!sdkAuthenticatedRef.current) {
11701
+ await reauthenticateSdk();
11702
+ return;
11703
+ }
11063
11704
  const result = await coordinator.verifyDocuments();
11064
11705
  if (!mounted) return;
11065
11706
  const outcome = typeof result === "string" ? result : result?.result;
@@ -11079,7 +11720,7 @@ function PayWithStripeLink({
11079
11720
  return () => {
11080
11721
  mounted = false;
11081
11722
  };
11082
- }, [step, coordinator]);
11723
+ }, [step, coordinator, reauthenticateSdk]);
11083
11724
  useEffect24(() => {
11084
11725
  if (step !== "wallet" || !oauthToken || !customerId) return;
11085
11726
  let mounted = true;
@@ -11105,6 +11746,10 @@ function PayWithStripeLink({
11105
11746
  if (!coordinator) {
11106
11747
  return;
11107
11748
  }
11749
+ if (!sdkAuthenticatedRef.current) {
11750
+ await reauthenticateSdk();
11751
+ return;
11752
+ }
11108
11753
  await coordinator.registerWalletAddress(onrampWalletAddress, network);
11109
11754
  }
11110
11755
  if (mounted) setRegisteredWalletKey(walletKeyFor(onrampWalletAddress));
@@ -11121,7 +11766,15 @@ function PayWithStripeLink({
11121
11766
  return () => {
11122
11767
  mounted = false;
11123
11768
  };
11124
- }, [step, oauthToken, customerId, publishableKey, onrampWalletAddress, coordinator]);
11769
+ }, [
11770
+ step,
11771
+ oauthToken,
11772
+ customerId,
11773
+ publishableKey,
11774
+ onrampWalletAddress,
11775
+ coordinator,
11776
+ reauthenticateSdk
11777
+ ]);
11125
11778
  useEffect24(() => {
11126
11779
  if (step !== "amount" && step !== "review") return;
11127
11780
  if (!customerId || !oauthToken || !onrampWalletAddress) return;
@@ -11150,6 +11803,10 @@ function PayWithStripeLink({
11150
11803
  if (coordinator && sdkAuthenticatedRef.current) {
11151
11804
  await coordinator.registerWalletAddress(onrampWalletAddress, network);
11152
11805
  if (!cancelled) setRegisteredWalletKey(walletKeyFor(onrampWalletAddress));
11806
+ } else if (coordinator && everAuthenticatedRef.current && !attemptedWalletReauthRef.current) {
11807
+ attemptedWalletReauthRef.current = true;
11808
+ await reauthenticateSdk();
11809
+ return;
11153
11810
  }
11154
11811
  } catch {
11155
11812
  }
@@ -11167,7 +11824,8 @@ function PayWithStripeLink({
11167
11824
  coordinator,
11168
11825
  stripeDestNetwork,
11169
11826
  stripeDestChainType,
11170
- publishableKey
11827
+ publishableKey,
11828
+ reauthenticateSdk
11171
11829
  ]);
11172
11830
  useEffect24(() => {
11173
11831
  if (step !== "payment" || !oauthToken || !customerId) return;
@@ -11264,6 +11922,32 @@ function PayWithStripeLink({
11264
11922
  if (!mounted) return;
11265
11923
  const tokenId = result.cryptoPaymentToken;
11266
11924
  let display = null;
11925
+ const pmd = result.paymentMethodDetails;
11926
+ if (pmd) {
11927
+ display = formatPaymentToken(pmd);
11928
+ }
11929
+ const singleUse = pmd?.type === "card" && !!pmd.card.wallet;
11930
+ const applyAndAdvance = () => {
11931
+ selectPaymentToken({
11932
+ id: tokenId,
11933
+ label: display?.label ?? "",
11934
+ sublabel: display?.sublabel ?? "",
11935
+ brand: display?.brand,
11936
+ singleUse
11937
+ });
11938
+ autoOpenedAddPaymentRef.current = false;
11939
+ setStep("amount");
11940
+ };
11941
+ if (display) {
11942
+ applyAndAdvance();
11943
+ if (customerId && accessTokenRef.current) {
11944
+ stripeListPaymentTokens(customerId, accessTokenRef.current, publishableKey).then((tokens) => {
11945
+ if (mounted) setPaymentTokens(tokens.data);
11946
+ }).catch(() => {
11947
+ });
11948
+ }
11949
+ return;
11950
+ }
11267
11951
  try {
11268
11952
  if (customerId && accessTokenRef.current) {
11269
11953
  const tokens = await stripeListPaymentTokens(
@@ -11271,28 +11955,15 @@ function PayWithStripeLink({
11271
11955
  accessTokenRef.current,
11272
11956
  publishableKey
11273
11957
  );
11958
+ if (!mounted) return;
11959
+ setPaymentTokens(tokens.data);
11274
11960
  const match = tokens.data.find((t13) => t13.id === tokenId);
11275
- if (match) {
11276
- display = formatPaymentToken(match);
11277
- setPaymentTokens(tokens.data);
11278
- }
11961
+ if (match) display = formatPaymentToken(match);
11279
11962
  }
11280
11963
  } catch {
11281
11964
  }
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");
11965
+ if (!mounted) return;
11966
+ applyAndAdvance();
11296
11967
  }
11297
11968
  );
11298
11969
  if (mounted && el) {
@@ -11329,6 +12000,10 @@ function PayWithStripeLink({
11329
12000
  const handleCheckout = async () => {
11330
12001
  if (!amount || !oauthToken || !customerId || !selectedPaymentToken || !onrampWalletAddress || !isOnrampWalletRegistered || !coordinator)
11331
12002
  return;
12003
+ if (!sdkAuthenticatedRef.current) {
12004
+ await reauthenticateSdk();
12005
+ return;
12006
+ }
11332
12007
  const gen = ++checkoutGenRef.current;
11333
12008
  const isStale = () => checkoutGenRef.current !== gen;
11334
12009
  const surfaceStepUpOnAmount = (err, message, tier) => {
@@ -11440,6 +12115,7 @@ function PayWithStripeLink({
11440
12115
  executionReconciledSessionRef.current = null;
11441
12116
  setConfirmedSessionId(activeSessionId);
11442
12117
  setSessionStatus(STRIPE_SESSION_STATUS.FULFILLMENT_PROCESSING);
12118
+ if (selectedTokenSingleUseRef.current) clearSelectedPaymentToken();
11443
12119
  setStep("success");
11444
12120
  } else if (lastError === "quote_rate_drifted" || lastError === "charged_with_expired_quote") {
11445
12121
  setSessionForCheckout(null);
@@ -11491,6 +12167,25 @@ function PayWithStripeLink({
11491
12167
  sessionForCheckoutRef.current = s;
11492
12168
  setSessionForCheckoutState(s);
11493
12169
  };
12170
+ const loadAndSelectPreferredToken = useCallback4(async () => {
12171
+ if (!customerId) return;
12172
+ try {
12173
+ const existing = await withTokenRefresh(
12174
+ (tok) => stripeListPaymentTokens(customerId, tok, publishableKey)
12175
+ );
12176
+ setPaymentTokens(existing.data);
12177
+ const stored = getStoredSelectedToken(customerId);
12178
+ const merged = [
12179
+ ...existing.data.map((t13) => ({ id: t13.id, ...formatPaymentToken(t13) })),
12180
+ ...stored && !existing.data.some((t13) => t13.id === stored.id) ? [stored] : []
12181
+ ];
12182
+ if (merged.length > 0) {
12183
+ const preferred = stored && merged.find((m) => m.id === stored.id) || merged[0];
12184
+ selectPaymentToken(preferred);
12185
+ }
12186
+ } catch {
12187
+ }
12188
+ }, [customerId, publishableKey, selectPaymentToken, withTokenRefresh]);
11494
12189
  const handleDone = () => {
11495
12190
  handledTerminalSessionRef.current = null;
11496
12191
  executionReconciledSessionRef.current = null;
@@ -11498,6 +12193,7 @@ function PayWithStripeLink({
11498
12193
  setSessionStatus(null);
11499
12194
  setSessionForCheckout(null);
11500
12195
  setStep("amount");
12196
+ if (!selectedPaymentToken) void loadAndSelectPreferredToken();
11501
12197
  };
11502
12198
  const [quoteError, setQuoteError] = useState29(null);
11503
12199
  const [quoteNonce, setQuoteNonce] = useState29(0);
@@ -12337,7 +13033,8 @@ function PayWithStripeLink({
12337
13033
  {
12338
13034
  brand: display.brand,
12339
13035
  size: 28,
12340
- fallbackColor: colors2.foregroundMuted
13036
+ fallbackColor: colors2.foregroundMuted,
13037
+ monochromeColor: monochromeOn(components.card.backgroundColor)
12341
13038
  }
12342
13039
  )
12343
13040
  }
@@ -12922,7 +13619,8 @@ function PayWithStripeLink({
12922
13619
  {
12923
13620
  brand: paymentDisplay.brand,
12924
13621
  size: 20,
12925
- fallbackColor: colors2.foregroundMuted
13622
+ fallbackColor: colors2.foregroundMuted,
13623
+ monochromeColor: monochromeOn(components.card.backgroundColor)
12926
13624
  }
12927
13625
  ),
12928
13626
  /* @__PURE__ */ jsx44(
@@ -13408,7 +14106,7 @@ function PayWithStripeLink({
13408
14106
  }
13409
14107
 
13410
14108
  // src/components/deposits/CoinbaseConnect.tsx
13411
- import { useState as useState30, useEffect as useEffect25, useCallback as useCallback5, useMemo as useMemo6, useRef as useRef9 } from "react";
14109
+ import { useState as useState30, useEffect as useEffect25, useCallback as useCallback5, useMemo as useMemo7, useRef as useRef9 } from "react";
13412
14110
  import {
13413
14111
  ChevronRight as ChevronRight15,
13414
14112
  ChevronDown as ChevronDown4,
@@ -13417,7 +14115,7 @@ import {
13417
14115
  Hand,
13418
14116
  Loader2 as Loader26,
13419
14117
  CheckCircle2 as CheckCircle23,
13420
- AlertCircle as AlertCircle2
14118
+ AlertCircle
13421
14119
  } from "lucide-react";
13422
14120
  import {
13423
14121
  getIntegrationExchanges,
@@ -13430,8 +14128,26 @@ import {
13430
14128
  IntegrationProvider
13431
14129
  } from "@unifold/core";
13432
14130
 
13433
- // src/hooks/use-supported-deposit-tokens.ts
14131
+ // src/hooks/use-project-config.ts
13434
14132
  import { useQuery as useQuery8 } from "@tanstack/react-query";
14133
+ import { getProjectConfig } from "@unifold/core";
14134
+ function useProjectConfig({
14135
+ publishableKey,
14136
+ enabled = true
14137
+ }) {
14138
+ const { data: projectConfig, isLoading } = useQuery8({
14139
+ queryKey: ["unifold", "projectConfig", publishableKey],
14140
+ queryFn: () => getProjectConfig(publishableKey),
14141
+ enabled,
14142
+ staleTime: 1e3 * 60 * 30,
14143
+ refetchOnMount: true,
14144
+ refetchOnWindowFocus: true
14145
+ });
14146
+ return { projectConfig, isLoading };
14147
+ }
14148
+
14149
+ // src/hooks/use-supported-deposit-tokens.ts
14150
+ import { useQuery as useQuery9 } from "@tanstack/react-query";
13435
14151
  import {
13436
14152
  getSupportedDepositTokens
13437
14153
  } from "@unifold/core";
@@ -13446,7 +14162,7 @@ function useSupportedDepositTokens(publishableKey, options) {
13446
14162
  ...options?.product_type ? { product_type: options.product_type } : {}
13447
14163
  };
13448
14164
  const hasFilteredOptions = Object.keys(filteredOptions).length > 0;
13449
- return useQuery8({
14165
+ return useQuery9({
13450
14166
  queryKey: [
13451
14167
  "unifold",
13452
14168
  "supportedDepositTokens",
@@ -13467,7 +14183,7 @@ function useSupportedDepositTokens(publishableKey, options) {
13467
14183
  }
13468
14184
 
13469
14185
  // src/hooks/use-integration-transfer-default-token.ts
13470
- import { useQuery as useQuery9 } from "@tanstack/react-query";
14186
+ import { useQuery as useQuery10 } from "@tanstack/react-query";
13471
14187
  import {
13472
14188
  getIntegrationTransferDefaultToken
13473
14189
  } from "@unifold/core";
@@ -13476,7 +14192,7 @@ function useIntegrationTransferDefaultToken({
13476
14192
  publishableKey,
13477
14193
  enabled = true
13478
14194
  }) {
13479
- return useQuery9({
14195
+ return useQuery10({
13480
14196
  queryKey: [
13481
14197
  "unifold",
13482
14198
  "integrationTransferDefaultToken",
@@ -13557,12 +14273,12 @@ function CoinbaseConnect({
13557
14273
  destination_chain_id: destinationChainId,
13558
14274
  destination_chain_type: destinationChainType
13559
14275
  });
13560
- const supportedSymbols = useMemo6(() => {
14276
+ const supportedSymbols = useMemo7(() => {
13561
14277
  const set = /* @__PURE__ */ new Set();
13562
14278
  supportedTokensData?.data.forEach((token) => set.add(token.symbol.toLowerCase()));
13563
14279
  return set;
13564
14280
  }, [supportedTokensData]);
13565
- const stablecoinSymbols = useMemo6(() => {
14281
+ const stablecoinSymbols = useMemo7(() => {
13566
14282
  const set = /* @__PURE__ */ new Set();
13567
14283
  supportedTokensData?.data.forEach((token) => {
13568
14284
  if (token.is_stablecoin) set.add(token.symbol.toLowerCase());
@@ -13595,12 +14311,12 @@ function CoinbaseConnect({
13595
14311
  const [transferDepositWalletId, setTransferDepositWalletId] = useState30(
13596
14312
  void 0
13597
14313
  );
13598
- const exchangeSupportedCurrencies = useMemo6(() => {
14314
+ const exchangeSupportedCurrencies = useMemo7(() => {
13599
14315
  const set = /* @__PURE__ */ new Set();
13600
14316
  selectedExchange?.supported_currencies.forEach((c) => set.add(c.toLowerCase()));
13601
14317
  return set;
13602
14318
  }, [selectedExchange]);
13603
- const defaultTokenParams = useMemo6(
14319
+ const defaultTokenParams = useMemo7(
13604
14320
  () => selectedAsset ? {
13605
14321
  integration_provider: IntegrationProvider.COINBASE,
13606
14322
  source_currency: selectedAsset.currency.toLowerCase(),
@@ -13623,7 +14339,7 @@ function CoinbaseConnect({
13623
14339
  params: defaultTokenParams,
13624
14340
  publishableKey
13625
14341
  });
13626
- const defaultSourceCurrency = useMemo6(
14342
+ const defaultSourceCurrency = useMemo7(
13627
14343
  () => resolveDefaultSourceSymbol(supportedTokensData?.data, {
13628
14344
  defaultSourceChainType,
13629
14345
  defaultSourceChainId,
@@ -13638,7 +14354,7 @@ function CoinbaseConnect({
13638
14354
  defaultSourceSymbol
13639
14355
  ]
13640
14356
  );
13641
- const sortedHoldings = useMemo6(() => {
14357
+ const sortedHoldings = useMemo7(() => {
13642
14358
  const supported = [];
13643
14359
  const unsupported = [];
13644
14360
  holdings.forEach((account) => {
@@ -13658,7 +14374,7 @@ function CoinbaseConnect({
13658
14374
  }
13659
14375
  return [...supported, ...unsupported];
13660
14376
  }, [holdings, supportedSymbols, exchangeSupportedCurrencies, defaultSourceCurrency]);
13661
- const selectedHoldingIsSupported = useMemo6(() => {
14377
+ const selectedHoldingIsSupported = useMemo7(() => {
13662
14378
  if (!selectedHolding) return false;
13663
14379
  const currencyLower = selectedHolding.currency.toLowerCase();
13664
14380
  return (supportedSymbols.size === 0 || supportedSymbols.has(currencyLower)) && (exchangeSupportedCurrencies.size === 0 || exchangeSupportedCurrencies.has(currencyLower));
@@ -13805,7 +14521,7 @@ function CoinbaseConnect({
13805
14521
  if (pollRef.current) clearInterval(pollRef.current);
13806
14522
  };
13807
14523
  }, []);
13808
- const minDepositUsd = useMemo6(() => {
14524
+ const minDepositUsd = useMemo7(() => {
13809
14525
  if (!selectedAsset || !defaultTokenData) return 0;
13810
14526
  const supportedToken = supportedTokensData?.data.find(
13811
14527
  (t14) => t14.symbol.toLowerCase() === selectedAsset.currency.toLowerCase()
@@ -13816,7 +14532,7 @@ function CoinbaseConnect({
13816
14532
  );
13817
14533
  return matchingChain?.minimum_deposit_amount_usd ?? Math.min(...supportedToken.chains.map((c) => c.minimum_deposit_amount_usd));
13818
14534
  }, [selectedAsset, supportedTokensData, defaultTokenData]);
13819
- const estimatedProcessingTime = useMemo6(() => {
14535
+ const estimatedProcessingTime = useMemo7(() => {
13820
14536
  if (!selectedAsset || !defaultTokenData) return null;
13821
14537
  const supportedToken = supportedTokensData?.data.find(
13822
14538
  (t14) => t14.symbol.toLowerCase() === selectedAsset.currency.toLowerCase()
@@ -15409,7 +16125,7 @@ function CoinbaseConnect({
15409
16125
  {
15410
16126
  className: "uf-w-16 uf-h-16 uf-rounded-full uf-flex uf-items-center uf-justify-center uf-mb-6",
15411
16127
  style: { backgroundColor: `${colors2.error}20` },
15412
- children: /* @__PURE__ */ jsx45(AlertCircle2, { className: "uf-w-8 uf-h-8", style: { color: colors2.error } })
16128
+ children: /* @__PURE__ */ jsx45(AlertCircle, { className: "uf-w-8 uf-h-8", style: { color: colors2.error } })
15413
16129
  }
15414
16130
  ),
15415
16131
  /* @__PURE__ */ jsx45(
@@ -15456,13 +16172,13 @@ function CoinbaseConnect({
15456
16172
  CoinbaseConnect.displayName = "CoinbaseConnect";
15457
16173
 
15458
16174
  // src/hooks/use-exchanges.ts
15459
- import { useQuery as useQuery10 } from "@tanstack/react-query";
16175
+ import { useQuery as useQuery11 } from "@tanstack/react-query";
15460
16176
  import { getExchanges } from "@unifold/core";
15461
16177
  function useExchanges({
15462
16178
  publishableKey,
15463
16179
  enabled = true
15464
16180
  }) {
15465
- const { data: exchanges = [], isLoading } = useQuery10({
16181
+ const { data: exchanges = [], isLoading } = useQuery11({
15466
16182
  queryKey: ["unifold", "exchanges", publishableKey],
15467
16183
  queryFn: () => getExchanges(void 0, publishableKey).then((res) => res.data),
15468
16184
  enabled,
@@ -15474,13 +16190,13 @@ function useExchanges({
15474
16190
  }
15475
16191
 
15476
16192
  // src/hooks/use-apple-pay-providers.ts
15477
- import { useQuery as useQuery11 } from "@tanstack/react-query";
16193
+ import { useQuery as useQuery12 } from "@tanstack/react-query";
15478
16194
  import { getApplePayProviders } from "@unifold/core";
15479
16195
  function useApplePayProviders({
15480
16196
  publishableKey,
15481
16197
  enabled = true
15482
16198
  }) {
15483
- const { data: providers, isLoading } = useQuery11({
16199
+ const { data: providers, isLoading } = useQuery12({
15484
16200
  queryKey: ["unifold", "applePayProviders", publishableKey],
15485
16201
  queryFn: () => getApplePayProviders(publishableKey),
15486
16202
  enabled,
@@ -15503,7 +16219,7 @@ import {
15503
16219
  } from "@unifold/core";
15504
16220
 
15505
16221
  // src/hooks/use-allowed-country.ts
15506
- import { useQuery as useQuery12 } from "@tanstack/react-query";
16222
+ import { useQuery as useQuery13 } from "@tanstack/react-query";
15507
16223
  import {
15508
16224
  getIpAddress as getIpAddress2,
15509
16225
  getProjectConfig as getProjectConfig2
@@ -15513,7 +16229,7 @@ function useAllowedCountry(publishableKey) {
15513
16229
  data: ipData,
15514
16230
  isLoading: isIpLoading,
15515
16231
  error: ipError
15516
- } = useQuery12({
16232
+ } = useQuery13({
15517
16233
  queryKey: ["unifold", "ipAddress"],
15518
16234
  queryFn: () => getIpAddress2(),
15519
16235
  refetchOnMount: false,
@@ -15528,7 +16244,7 @@ function useAllowedCountry(publishableKey) {
15528
16244
  data: configData,
15529
16245
  isLoading: isConfigLoading,
15530
16246
  error: configError
15531
- } = useQuery12({
16247
+ } = useQuery13({
15532
16248
  queryKey: ["unifold", "projectConfig", publishableKey],
15533
16249
  queryFn: () => getProjectConfig2(publishableKey),
15534
16250
  refetchOnMount: false,
@@ -15567,7 +16283,7 @@ function useAllowedCountry(publishableKey) {
15567
16283
  }
15568
16284
 
15569
16285
  // src/hooks/use-address-validation.ts
15570
- import { useQuery as useQuery13 } from "@tanstack/react-query";
16286
+ import { useQuery as useQuery14 } from "@tanstack/react-query";
15571
16287
  import {
15572
16288
  verifyRecipientAddress
15573
16289
  } from "@unifold/core";
@@ -15581,7 +16297,7 @@ function useAddressValidation({
15581
16297
  refetchOnMount = false
15582
16298
  }) {
15583
16299
  const shouldValidate = enabled && !!recipientAddress && !!destinationChainType && !!destinationChainId && !!destinationTokenAddress;
15584
- const { data, isLoading, error } = useQuery13({
16300
+ const { data, isLoading, error } = useQuery14({
15585
16301
  queryKey: [
15586
16302
  "unifold",
15587
16303
  "addressValidation",
@@ -15627,7 +16343,7 @@ function useAddressValidation({
15627
16343
  }
15628
16344
 
15629
16345
  // src/components/deposits/TransferCryptoSingleInput.tsx
15630
- import { useState as useState36, useEffect as useEffect30, useMemo as useMemo9 } from "react";
16346
+ import { useState as useState36, useEffect as useEffect30, useMemo as useMemo10 } from "react";
15631
16347
  import {
15632
16348
  ChevronDown as ChevronDown5,
15633
16349
  ChevronUp as ChevronUp3,
@@ -15986,7 +16702,7 @@ function DepositsModal({
15986
16702
  }
15987
16703
 
15988
16704
  // src/components/deposits/TokenSelectorSheet.tsx
15989
- import { useState as useState32, useMemo as useMemo8, useEffect as useEffect28 } from "react";
16705
+ import { useState as useState32, useMemo as useMemo9, useEffect as useEffect28 } from "react";
15990
16706
  import { ArrowLeft as ArrowLeft2, X as X4 } from "lucide-react";
15991
16707
  import Fuse from "fuse.js";
15992
16708
  import { jsx as jsx49, jsxs as jsxs45 } from "react/jsx-runtime";
@@ -16053,7 +16769,7 @@ function TokenSelectorSheet({
16053
16769
  useEffect28(() => {
16054
16770
  setRecentTokens(getRecentTokens());
16055
16771
  }, []);
16056
- const allOptions = useMemo8(() => {
16772
+ const allOptions = useMemo9(() => {
16057
16773
  const options = [];
16058
16774
  tokens.forEach((token) => {
16059
16775
  token.chains.forEach((chain) => {
@@ -16062,7 +16778,7 @@ function TokenSelectorSheet({
16062
16778
  });
16063
16779
  return options;
16064
16780
  }, [tokens]);
16065
- const quickSelectOptions = useMemo8(() => {
16781
+ const quickSelectOptions = useMemo9(() => {
16066
16782
  const result = [];
16067
16783
  const seen = /* @__PURE__ */ new Set();
16068
16784
  const addOption = (symbol, chainType, chainId, isRecent) => {
@@ -16094,7 +16810,7 @@ function TokenSelectorSheet({
16094
16810
  });
16095
16811
  setRecentTokens(updated);
16096
16812
  };
16097
- const fuse = useMemo8(
16813
+ const fuse = useMemo9(
16098
16814
  () => new Fuse(allOptions, {
16099
16815
  keys: [
16100
16816
  { name: "token.symbol", weight: 2 },
@@ -16107,7 +16823,7 @@ function TokenSelectorSheet({
16107
16823
  }),
16108
16824
  [allOptions]
16109
16825
  );
16110
- const filteredOptions = useMemo8(() => {
16826
+ const filteredOptions = useMemo9(() => {
16111
16827
  if (!searchQuery.trim()) return allOptions;
16112
16828
  const query = searchQuery.trim();
16113
16829
  const results = fuse.search(query);
@@ -16943,7 +17659,7 @@ import {
16943
17659
  } from "@unifold/core";
16944
17660
 
16945
17661
  // src/hooks/use-hypercore-activation.ts
16946
- import { useQuery as useQuery14 } from "@tanstack/react-query";
17662
+ import { useQuery as useQuery15 } from "@tanstack/react-query";
16947
17663
  import { checkHypercoreActivation } from "@unifold/core";
16948
17664
 
16949
17665
  // src/lib/constants.ts
@@ -16962,7 +17678,7 @@ function useHypercoreActivation(params) {
16962
17678
  const recipient = recipientAddress?.trim() ?? "";
16963
17679
  const source = sourceAddress?.trim() ?? "";
16964
17680
  const hasAddresses = !!recipient && !!source;
16965
- const { data, isLoading } = useQuery14({
17681
+ const { data, isLoading } = useQuery15({
16966
17682
  queryKey: ["unifold", "hypercoreActivation", source, recipient, publishableKey],
16967
17683
  queryFn: () => checkHypercoreActivation(
16968
17684
  {
@@ -17095,7 +17811,7 @@ function TransferCryptoSingleInput({
17095
17811
  const wallets = externalWallets?.length ? externalWallets : depositAddressResponse?.data ?? [];
17096
17812
  const loading = externalWallets?.length ? false : walletsLoading;
17097
17813
  const error = walletsError?.message ?? null;
17098
- const allAvailableChains = useMemo9(() => {
17814
+ const allAvailableChains = useMemo10(() => {
17099
17815
  const chainsMap = /* @__PURE__ */ new Map();
17100
17816
  supportedTokens.forEach((t13) => {
17101
17817
  t13.chains.forEach((c) => {
@@ -17690,7 +18406,7 @@ function TransferCryptoSingleInput({
17690
18406
  }
17691
18407
 
17692
18408
  // src/components/deposits/TransferCryptoDoubleInput.tsx
17693
- import { useState as useState37, useEffect as useEffect31, useMemo as useMemo10 } from "react";
18409
+ import { useState as useState37, useEffect as useEffect31, useMemo as useMemo11 } from "react";
17694
18410
  import {
17695
18411
  ChevronDown as ChevronDown7,
17696
18412
  ChevronUp as ChevronUp5,
@@ -17899,7 +18615,7 @@ function TransferCryptoDoubleInput({
17899
18615
  const wallets = externalWallets?.length ? externalWallets : depositAddressResponse?.data ?? [];
17900
18616
  const loading = externalWallets?.length ? false : walletsLoading;
17901
18617
  const error = walletsError?.message ?? null;
17902
- const allAvailableChains = useMemo10(() => {
18618
+ const allAvailableChains = useMemo11(() => {
17903
18619
  const chainsMap = /* @__PURE__ */ new Map();
17904
18620
  supportedTokens.forEach((t13) => {
17905
18621
  t13.chains.forEach((c) => {
@@ -18491,7 +19207,7 @@ async function sendHypercoreEvmTransfer(params) {
18491
19207
  }
18492
19208
 
18493
19209
  // src/hooks/use-deposit-quote.ts
18494
- import { useQuery as useQuery15 } from "@tanstack/react-query";
19210
+ import { useQuery as useQuery16 } from "@tanstack/react-query";
18495
19211
  import { getDepositQuote } from "@unifold/core";
18496
19212
  function useDepositQuote(params) {
18497
19213
  const {
@@ -18518,7 +19234,7 @@ function useDepositQuote(params) {
18518
19234
  ...adjustForSlippage ? { adjust_for_slippage: true } : {},
18519
19235
  ...stablecoinParity ? { stablecoin_parity: true } : {}
18520
19236
  };
18521
- return useQuery15({
19237
+ return useQuery16({
18522
19238
  queryKey: [
18523
19239
  "unifold",
18524
19240
  "depositQuote",
@@ -18546,13 +19262,13 @@ function useDepositQuote(params) {
18546
19262
  }
18547
19263
 
18548
19264
  // src/hooks/use-external-wallets.ts
18549
- import { useQuery as useQuery16 } from "@tanstack/react-query";
19265
+ import { useQuery as useQuery17 } from "@tanstack/react-query";
18550
19266
  import { getExternalWallets } from "@unifold/core";
18551
19267
  function useExternalWallets({
18552
19268
  publishableKey,
18553
19269
  enabled = true
18554
19270
  }) {
18555
- const { data: wallets = [], isLoading } = useQuery16({
19271
+ const { data: wallets = [], isLoading } = useQuery17({
18556
19272
  queryKey: ["unifold", "external-wallets", publishableKey],
18557
19273
  queryFn: () => getExternalWallets(publishableKey).then((res) => res.data),
18558
19274
  enabled: enabled && !!publishableKey,
@@ -21187,10 +21903,9 @@ function DepositModal({
21187
21903
  applePaySubTitle = "Instant",
21188
21904
  enableBankTransfer,
21189
21905
  enableStripeLink = false,
21190
- email: emailProp,
21906
+ userEmail,
21191
21907
  hideDepositFlowInfo = false,
21192
21908
  hideDisplayDescription = false,
21193
- externalUserEmail,
21194
21909
  onDepositSuccess,
21195
21910
  onDepositError,
21196
21911
  onEvent,
@@ -21222,7 +21937,7 @@ function DepositModal({
21222
21937
  (method) => onDepositError ? (error) => onDepositError({ ...error, method }) : void 0,
21223
21938
  [onDepositError]
21224
21939
  );
21225
- const effectiveInitialScreen = useMemo12(() => {
21940
+ const effectiveInitialScreen = useMemo13(() => {
21226
21941
  const s = initialScreen ?? "main";
21227
21942
  if (s === "tracker" && hideDepositTracker === true) return "main";
21228
21943
  if (s === "cashapp" && enableCashApp === false) return "main";
@@ -21621,11 +22336,18 @@ function DepositModal({
21621
22336
  return "Success";
21622
22337
  case "guest_limit_reached":
21623
22338
  return "Limit reached";
22339
+ case "limit_upgrade_intro":
22340
+ case "limit_upgrade_failed":
22341
+ return "Limit reached";
22342
+ case "limit_upgrade_form":
22343
+ return "Verify identity";
22344
+ case "limit_upgrade_submitting":
22345
+ return "Verifying";
21624
22346
  default:
21625
22347
  return "Pay with Apple Pay";
21626
22348
  }
21627
22349
  })();
21628
- const applePayShowBack = sessionOpenedFromMenu || applePayView === "phone_input" || applePayView === "email_otp" || applePayView === "phone_otp" || applePayView === "submitting_session" || applePayView === "checking_deposit";
22350
+ 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
22351
  const handleBack = () => {
21630
22352
  if (view === "card" && cardView === "quotes") {
21631
22353
  setCardView("amount");
@@ -22304,7 +23026,7 @@ function DepositModal({
22304
23026
  destinationChainId,
22305
23027
  destinationTokenAddress,
22306
23028
  wallets,
22307
- email: emailProp,
23029
+ email: userEmail,
22308
23030
  iconUrl: projectConfig?.asset_cdn_url ? `${projectConfig.asset_cdn_url}/api/public/icons/onramps/svg/link.svg` : void 0,
22309
23031
  step: stripeLinkStep,
22310
23032
  onStepChange: setStripeLinkStep,
@@ -22367,13 +23089,10 @@ function DepositModal({
22367
23089
  ref: applePayHandleRef,
22368
23090
  userId,
22369
23091
  publishableKey,
22370
- recipientAddress,
22371
23092
  destinationChainType,
22372
23093
  destinationChainId,
22373
23094
  destinationTokenAddress,
22374
- destinationTokenSymbol,
22375
- externalUserEmail,
22376
- enableApplePay,
23095
+ userEmail,
22377
23096
  wallets,
22378
23097
  onViewChange: setApplePayView,
22379
23098
  onEvent,
@@ -22400,16 +23119,16 @@ function DepositModal({
22400
23119
  }
22401
23120
 
22402
23121
  // 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";
23122
+ import { useState as useState41, useEffect as useEffect35, useLayoutEffect as useLayoutEffect3, useCallback as useCallback9, useRef as useRef13, useMemo as useMemo14 } from "react";
22404
23123
  import { AlertTriangle as AlertTriangle4, ChevronRight as ChevronRight19 } from "lucide-react";
22405
23124
 
22406
23125
  // src/hooks/use-payment-intent.ts
22407
- import { useQuery as useQuery17 } from "@tanstack/react-query";
23126
+ import { useQuery as useQuery18 } from "@tanstack/react-query";
22408
23127
  import { retrievePaymentIntent } from "@unifold/core";
22409
23128
  var TERMINAL_STATUSES = /* @__PURE__ */ new Set(["succeeded", "expired", "refunded", "canceled"]);
22410
23129
  function usePaymentIntent(params) {
22411
23130
  const { clientSecret, publishableKey, enabled = true, pollingInterval = 3e3 } = params;
22412
- return useQuery17({
23131
+ return useQuery18({
22413
23132
  queryKey: ["unifold", "paymentIntent", clientSecret, publishableKey],
22414
23133
  queryFn: () => retrievePaymentIntent(clientSecret, publishableKey),
22415
23134
  enabled: enabled && !!clientSecret && !!publishableKey,
@@ -22585,11 +23304,11 @@ function CheckoutModal({
22585
23304
  );
22586
23305
  }
22587
23306
  }, [emitCheckoutSuccess, paymentIntent, view]);
22588
- const wallets = useMemo13(() => {
23307
+ const wallets = useMemo14(() => {
22589
23308
  if (!paymentIntent) return [];
22590
23309
  return mapDepositAddressesToWallets(paymentIntent.deposit_addresses, paymentIntent);
22591
23310
  }, [paymentIntent]);
22592
- const formatCryptoAmount = useMemo13(() => {
23311
+ const formatCryptoAmount = useMemo14(() => {
22593
23312
  if (!paymentIntent) return (_) => "";
22594
23313
  const decimals = paymentIntent.destination_token_decimals ?? 6;
22595
23314
  const symbol = paymentIntent.currency.toUpperCase();
@@ -22599,7 +23318,7 @@ function CheckoutModal({
22599
23318
  return `${formatted} ${symbol}`;
22600
23319
  };
22601
23320
  }, [paymentIntent]);
22602
- const remainingAmountUsd = useMemo13(() => {
23321
+ const remainingAmountUsd = useMemo14(() => {
22603
23322
  if (!paymentIntent) return void 0;
22604
23323
  const total = parseFloat(paymentIntent.destination_amount_usd || paymentIntent.amount_usd);
22605
23324
  const received = parseFloat(
@@ -22611,7 +23330,7 @@ function CheckoutModal({
22611
23330
  return remaining > 0 ? remaining.toFixed(2) : "0.00";
22612
23331
  }, [paymentIntent]);
22613
23332
  const [selectedSource, setSelectedSource] = useState41(null);
22614
- const remainingDestinationAmount = useMemo13(() => {
23333
+ const remainingDestinationAmount = useMemo14(() => {
22615
23334
  if (!paymentIntent) return "0";
22616
23335
  const remaining = BigInt(paymentIntent.destination_amount) - BigInt(paymentIntent.destination_amount_received);
22617
23336
  return remaining > 0n ? remaining.toString() : "0";
@@ -22633,7 +23352,7 @@ function CheckoutModal({
22633
23352
  stablecoinParity: paymentIntent?.stablecoin_parity ?? false,
22634
23353
  enabled: open && view === "transfer" && !!paymentIntent && !!selectedSource && remainingDestinationAmount !== "0"
22635
23354
  });
22636
- const effectiveCheckoutQuote = useMemo13(() => {
23355
+ const effectiveCheckoutQuote = useMemo14(() => {
22637
23356
  if (!sourceQuote || !selectedSource) return null;
22638
23357
  const baseQuote = {
22639
23358
  sourceAmount: sourceQuote.source_amount,
@@ -23101,12 +23820,12 @@ import { useState as useState45, useEffect as useEffect39, useLayoutEffect as us
23101
23820
  import { AlertTriangle as AlertTriangle6, ChevronRight as ChevronRight21, Clock as Clock6 } from "lucide-react";
23102
23821
 
23103
23822
  // src/hooks/use-supported-destination-tokens.ts
23104
- import { useQuery as useQuery18 } from "@tanstack/react-query";
23823
+ import { useQuery as useQuery19 } from "@tanstack/react-query";
23105
23824
  import {
23106
23825
  getSupportedDestinationTokens
23107
23826
  } from "@unifold/core";
23108
23827
  function useSupportedDestinationTokens(publishableKey, enabled = true) {
23109
- return useQuery18({
23828
+ return useQuery19({
23110
23829
  queryKey: ["unifold", "supportedDestinationTokens", publishableKey],
23111
23830
  queryFn: () => getSupportedDestinationTokens(publishableKey),
23112
23831
  staleTime: 1e3 * 60 * 5,
@@ -23135,7 +23854,7 @@ function useDefaultDestinationToken({
23135
23854
  }
23136
23855
 
23137
23856
  // src/hooks/use-source-token-validation.ts
23138
- import { useQuery as useQuery19 } from "@tanstack/react-query";
23857
+ import { useQuery as useQuery20 } from "@tanstack/react-query";
23139
23858
  import { getSupportedDepositTokens as getSupportedDepositTokens3 } from "@unifold/core";
23140
23859
  function useSourceTokenValidation(params) {
23141
23860
  const {
@@ -23147,7 +23866,7 @@ function useSourceTokenValidation(params) {
23147
23866
  enabled = true
23148
23867
  } = params;
23149
23868
  const hasParams = !!sourceChainType && !!sourceChainId && !!sourceTokenAddress;
23150
- return useQuery19({
23869
+ return useQuery20({
23151
23870
  queryKey: [
23152
23871
  "unifold",
23153
23872
  "sourceTokenValidation",
@@ -23195,12 +23914,12 @@ function useSourceTokenValidation(params) {
23195
23914
  }
23196
23915
 
23197
23916
  // src/hooks/use-address-balance.ts
23198
- import { useQuery as useQuery20 } from "@tanstack/react-query";
23917
+ import { useQuery as useQuery21 } from "@tanstack/react-query";
23199
23918
  import { getAddressBalance as getAddressBalance2 } from "@unifold/core";
23200
23919
  function useAddressBalance(params) {
23201
23920
  const { address, chainType, chainId, tokenAddress, publishableKey, enabled = true } = params;
23202
23921
  const hasParams = !!address && !!chainType && !!chainId && !!tokenAddress;
23203
- return useQuery20({
23922
+ return useQuery21({
23204
23923
  queryKey: [
23205
23924
  "unifold",
23206
23925
  "addressBalance",
@@ -23256,11 +23975,11 @@ function useAddressBalance(params) {
23256
23975
  }
23257
23976
 
23258
23977
  // src/hooks/use-executions.ts
23259
- import { useQuery as useQuery21 } from "@tanstack/react-query";
23978
+ import { useQuery as useQuery22 } from "@tanstack/react-query";
23260
23979
  import { queryExecutions as queryExecutions4, ActionType as ActionType4 } from "@unifold/core";
23261
23980
  function useExecutions(userId, publishableKey, options) {
23262
23981
  const actionType = options?.actionType ?? ActionType4.Deposit;
23263
- return useQuery21({
23982
+ return useQuery22({
23264
23983
  queryKey: ["unifold", "executions", actionType, userId, publishableKey],
23265
23984
  queryFn: () => queryExecutions4(userId, publishableKey, actionType),
23266
23985
  enabled: (options?.enabled ?? true) && !!userId,
@@ -23591,7 +24310,7 @@ function WithdrawDoubleInput({
23591
24310
  }
23592
24311
 
23593
24312
  // src/components/withdrawals/WithdrawForm.tsx
23594
- import { useState as useState43, useCallback as useCallback10, useMemo as useMemo15, useEffect as useEffect37 } from "react";
24313
+ import { useState as useState43, useCallback as useCallback10, useMemo as useMemo16, useEffect as useEffect37 } from "react";
23595
24314
  import {
23596
24315
  AlertTriangle as AlertTriangle5,
23597
24316
  ArrowUpDown,
@@ -23609,7 +24328,7 @@ import {
23609
24328
  } from "@unifold/core";
23610
24329
 
23611
24330
  // src/hooks/use-verify-recipient-address.ts
23612
- import { useQuery as useQuery22 } from "@tanstack/react-query";
24331
+ import { useQuery as useQuery23 } from "@tanstack/react-query";
23613
24332
  import { verifyRecipientAddress as verifyRecipientAddress2 } from "@unifold/core";
23614
24333
  function useVerifyRecipientAddress(params) {
23615
24334
  const {
@@ -23622,7 +24341,7 @@ function useVerifyRecipientAddress(params) {
23622
24341
  } = params;
23623
24342
  const trimmedAddress = recipientAddress?.trim() || "";
23624
24343
  const hasAllParams = !!chainType && !!chainId && !!tokenAddress && trimmedAddress.length > 0;
23625
- return useQuery22({
24344
+ return useQuery23({
23626
24345
  queryKey: [
23627
24346
  "unifold",
23628
24347
  "verifyRecipientAddress",
@@ -23869,11 +24588,11 @@ async function detectBrowserWallet(chainType, senderAddress) {
23869
24588
  }
23870
24589
 
23871
24590
  // src/hooks/use-hypercore-withdraw-activation.ts
23872
- import { useMemo as useMemo14 } from "react";
24591
+ import { useMemo as useMemo15 } from "react";
23873
24592
  import { ActionType as ActionType6 } from "@unifold/core";
23874
24593
 
23875
24594
  // src/hooks/use-get-deposit-address.ts
23876
- import { useQuery as useQuery23 } from "@tanstack/react-query";
24595
+ import { useQuery as useQuery24 } from "@tanstack/react-query";
23877
24596
  import { getDepositAddress } from "@unifold/core";
23878
24597
  function useGetDepositAddress(params) {
23879
24598
  const {
@@ -23887,7 +24606,7 @@ function useGetDepositAddress(params) {
23887
24606
  enabled = true
23888
24607
  } = params;
23889
24608
  const canFire = !!userId && !!recipientAddress && !!destinationChainType && !!destinationChainId && !!destinationTokenAddress;
23890
- return useQuery23({
24609
+ return useQuery24({
23891
24610
  queryKey: [
23892
24611
  "unifold",
23893
24612
  "getDepositAddress",
@@ -23956,7 +24675,7 @@ function useHypercoreWithdrawActivation(params) {
23956
24675
  actionType: ActionType6.Withdraw,
23957
24676
  enabled: enabled && isHypercore(sourceChainId)
23958
24677
  });
23959
- const depositWalletAddress = useMemo14(() => {
24678
+ const depositWalletAddress = useMemo15(() => {
23960
24679
  const wallets = depositWalletLookup.data?.data ?? [];
23961
24680
  return wallets.find((w) => w.chain_type === sourceChainType)?.address;
23962
24681
  }, [depositWalletLookup.data, sourceChainType]);
@@ -24078,7 +24797,7 @@ function WithdrawForm({
24078
24797
  enabled: debouncedAddress.length > 5 && !!selectedChain
24079
24798
  });
24080
24799
  const isDebouncing = trimmedAddress !== debouncedAddress;
24081
- const addressError = useMemo15(() => {
24800
+ const addressError = useMemo16(() => {
24082
24801
  if (!trimmedAddress || trimmedAddress.length <= 5) return null;
24083
24802
  if (isDebouncing || isVerifyingAddress) return null;
24084
24803
  if (verifyError) return t10.invalidAddress;
@@ -24112,33 +24831,33 @@ function WithdrawForm({
24112
24831
  destinationTokenAddress: selectedChain?.token_address,
24113
24832
  enabled: isAddressValid
24114
24833
  });
24115
- const exchangeRate = useMemo15(() => {
24834
+ const exchangeRate = useMemo16(() => {
24116
24835
  if (!balanceData?.exchangeRate) return 0;
24117
24836
  return parseFloat(balanceData.exchangeRate);
24118
24837
  }, [balanceData]);
24119
- const balanceCrypto = useMemo15(() => {
24838
+ const balanceCrypto = useMemo16(() => {
24120
24839
  if (!balanceData?.balanceHuman) return 0;
24121
24840
  return parseFloat(balanceData.balanceHuman);
24122
24841
  }, [balanceData]);
24123
- const balanceUsdNum = useMemo15(() => {
24842
+ const balanceUsdNum = useMemo16(() => {
24124
24843
  if (!balanceData?.balanceUsd) return 0;
24125
24844
  return parseFloat(balanceData.balanceUsd);
24126
24845
  }, [balanceData]);
24127
24846
  const tokenSymbol = sourceTokenSymbol || balanceData?.symbol || "TOKEN";
24128
24847
  const sourceDecimals = balanceData?.decimals ?? 6;
24129
- const cryptoAmountFromInput = useMemo15(() => {
24848
+ const cryptoAmountFromInput = useMemo16(() => {
24130
24849
  const val = parseFloat(amount);
24131
24850
  if (!val || val <= 0) return 0;
24132
24851
  if (inputUnit === "crypto") return val;
24133
24852
  return exchangeRate > 0 ? val / exchangeRate : 0;
24134
24853
  }, [amount, inputUnit, exchangeRate]);
24135
- const fiatAmountFromInput = useMemo15(() => {
24854
+ const fiatAmountFromInput = useMemo16(() => {
24136
24855
  const val = parseFloat(amount);
24137
24856
  if (!val || val <= 0) return 0;
24138
24857
  if (inputUnit === "fiat") return val;
24139
24858
  return val * exchangeRate;
24140
24859
  }, [amount, inputUnit, exchangeRate]);
24141
- const convertedDisplay = useMemo15(() => {
24860
+ const convertedDisplay = useMemo16(() => {
24142
24861
  if (!amount || parseFloat(amount) <= 0) return null;
24143
24862
  if (inputUnit === "crypto") {
24144
24863
  return `$${fiatAmountFromInput.toLocaleString(void 0, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`;
@@ -24157,7 +24876,7 @@ function WithdrawForm({
24157
24876
  isMaxed,
24158
24877
  isStablecoin
24159
24878
  ]);
24160
- const balanceDisplay = useMemo15(() => {
24879
+ const balanceDisplay = useMemo16(() => {
24161
24880
  if (isLoadingBalance || !balanceData) return null;
24162
24881
  if (inputUnit === "crypto") {
24163
24882
  const displayDecimals = isStablecoin ? 2 : 6;
@@ -25340,7 +26059,7 @@ function WithdrawModal({
25340
26059
  }
25341
26060
 
25342
26061
  // src/components/withdrawals/WithdrawTokenSelector.tsx
25343
- import { useState as useState46, useMemo as useMemo16 } from "react";
26062
+ import { useState as useState46, useMemo as useMemo17 } from "react";
25344
26063
  import { Search } from "lucide-react";
25345
26064
  import Fuse2 from "fuse.js";
25346
26065
  import { jsx as jsx70, jsxs as jsxs64 } from "react/jsx-runtime";
@@ -25349,7 +26068,7 @@ function WithdrawTokenSelector({ tokens, onSelect, onBack }) {
25349
26068
  const { themeClass, colors: colors2, fonts, components } = useTheme();
25350
26069
  const [searchQuery, setSearchQuery] = useState46("");
25351
26070
  const [hoveredKey, setHoveredKey] = useState46(null);
25352
- const allOptions = useMemo16(() => {
26071
+ const allOptions = useMemo17(() => {
25353
26072
  const options = [];
25354
26073
  tokens.forEach((token) => {
25355
26074
  token.chains.forEach((chain) => {
@@ -25358,7 +26077,7 @@ function WithdrawTokenSelector({ tokens, onSelect, onBack }) {
25358
26077
  });
25359
26078
  return options;
25360
26079
  }, [tokens]);
25361
- const fuse = useMemo16(
26080
+ const fuse = useMemo17(
25362
26081
  () => new Fuse2(allOptions, {
25363
26082
  keys: [
25364
26083
  { name: "token.symbol", weight: 2 },
@@ -25371,7 +26090,7 @@ function WithdrawTokenSelector({ tokens, onSelect, onBack }) {
25371
26090
  }),
25372
26091
  [allOptions]
25373
26092
  );
25374
- const filteredOptions = useMemo16(() => {
26093
+ const filteredOptions = useMemo17(() => {
25375
26094
  if (!searchQuery.trim()) return allOptions;
25376
26095
  const query = searchQuery.trim();
25377
26096
  const results = fuse.search(query);
@@ -25616,7 +26335,6 @@ export {
25616
26335
  getStoredApplePaySession,
25617
26336
  isHypercoreChain,
25618
26337
  isOnrampTokenFresh,
25619
- isPhoneVerificationFresh,
25620
26338
  mergeColors,
25621
26339
  resolveComponentTokens,
25622
26340
  sendEvmWithdraw,