@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.d.mts +17 -24
- package/dist/index.d.ts +17 -24
- package/dist/index.js +1764 -1049
- package/dist/index.mjs +1290 -572
- package/dist/styles-base.css +1 -1
- package/dist/styles.css +1 -1
- package/package.json +3 -3
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
|
|
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
|
|
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
|
|
3524
|
-
import { Loader2 as Loader22
|
|
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
|
|
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
|
|
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
|
|
3627
|
-
var
|
|
3628
|
-
function
|
|
3629
|
-
const
|
|
3630
|
-
|
|
3631
|
-
|
|
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
|
-
|
|
3634
|
-
if (
|
|
3635
|
-
|
|
3636
|
-
|
|
3637
|
-
|
|
3638
|
-
|
|
3639
|
-
|
|
3640
|
-
|
|
3641
|
-
|
|
3642
|
-
|
|
3643
|
-
|
|
3644
|
-
|
|
3645
|
-
|
|
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
|
-
|
|
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:
|
|
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(
|
|
3676
|
-
|
|
3677
|
-
|
|
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 {
|
|
3700
|
-
|
|
3852
|
+
const { userIpInfo, isLoading: userIpInfoLoading } = useUserIp();
|
|
3853
|
+
const {
|
|
3854
|
+
defaultToken,
|
|
3855
|
+
isLoading: defaultTokenLoading,
|
|
3856
|
+
isError: defaultTokenError
|
|
3857
|
+
} = useDefaultOnrampToken({
|
|
3701
3858
|
publishableKey,
|
|
3702
|
-
|
|
3703
|
-
|
|
3704
|
-
|
|
3705
|
-
|
|
3706
|
-
|
|
3859
|
+
tokenAddress: destinationTokenAddress,
|
|
3860
|
+
chainId: destinationChainId,
|
|
3861
|
+
chainType: destinationChainType,
|
|
3862
|
+
countryCode: userIpInfo?.alpha2,
|
|
3863
|
+
subdivisionCode: userIpInfo?.subdivisionCode ?? void 0
|
|
3707
3864
|
});
|
|
3708
|
-
const
|
|
3709
|
-
|
|
3710
|
-
if (!
|
|
3711
|
-
return getWalletByChainType2(
|
|
3712
|
-
}, [
|
|
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(
|
|
3737
|
-
|
|
3738
|
-
|
|
3739
|
-
|
|
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
|
|
3788
|
-
|
|
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 (
|
|
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 =
|
|
3877
|
-
const isContactValid =
|
|
3878
|
-
const
|
|
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
|
|
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,
|
|
4005
|
+
setStoredApplePaySession(userId, session);
|
|
3887
4006
|
},
|
|
3888
4007
|
[email, normalizedPhone, userId]
|
|
3889
4008
|
);
|
|
3890
|
-
const
|
|
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
|
-
|
|
3910
|
-
|
|
3911
|
-
|
|
3912
|
-
|
|
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
|
-
}, [
|
|
3935
|
-
const
|
|
3936
|
-
|
|
3937
|
-
|
|
3938
|
-
|
|
3939
|
-
|
|
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
|
-
|
|
3942
|
-
|
|
3943
|
-
|
|
3944
|
-
|
|
3945
|
-
|
|
3946
|
-
|
|
3947
|
-
|
|
3948
|
-
|
|
3949
|
-
|
|
3950
|
-
|
|
3951
|
-
|
|
3952
|
-
|
|
3953
|
-
|
|
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
|
-
|
|
3957
|
-
setEmailCode("");
|
|
4130
|
+
setLimitUpgradeError(null);
|
|
3958
4131
|
}
|
|
3959
|
-
|
|
3960
|
-
|
|
3961
|
-
|
|
3962
|
-
|
|
3963
|
-
|
|
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
|
-
|
|
4203
|
+
setUpgradeSubmitting(false);
|
|
3966
4204
|
}
|
|
3967
4205
|
}, [
|
|
3968
|
-
|
|
3969
|
-
|
|
3970
|
-
|
|
4206
|
+
upgradeSubmitting,
|
|
4207
|
+
dobInput,
|
|
4208
|
+
ssnLast4,
|
|
4209
|
+
normalizedPhone,
|
|
3971
4210
|
publishableKey,
|
|
3972
|
-
|
|
3973
|
-
|
|
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
|
-
|
|
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
|
-
|
|
4006
|
-
|
|
4007
|
-
|
|
4008
|
-
|
|
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
|
-
|
|
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
|
-
|
|
4034
|
-
|
|
4035
|
-
|
|
4036
|
-
)
|
|
4037
|
-
|
|
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:
|
|
4058
|
-
destination_network:
|
|
4310
|
+
destination_currency: defaultToken.destination_currency,
|
|
4311
|
+
destination_network: defaultToken.destination_network,
|
|
4059
4312
|
wallet_address: settlementAddress,
|
|
4060
4313
|
external_id: externalId,
|
|
4061
|
-
...
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
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 === "
|
|
4359
|
-
const
|
|
4360
|
-
const
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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:
|
|
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
|
|
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
|
|
4680
|
-
|
|
4681
|
-
|
|
4682
|
-
|
|
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
|
-
|
|
4696
|
-
|
|
4697
|
-
|
|
4698
|
-
|
|
4699
|
-
|
|
4700
|
-
|
|
4701
|
-
|
|
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
|
-
|
|
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
|
-
|
|
4759
|
-
|
|
4760
|
-
|
|
4761
|
-
|
|
4762
|
-
|
|
4763
|
-
|
|
4764
|
-
|
|
4765
|
-
|
|
4766
|
-
|
|
4767
|
-
|
|
4768
|
-
|
|
4769
|
-
|
|
4770
|
-
|
|
4771
|
-
|
|
4772
|
-
|
|
4773
|
-
|
|
4774
|
-
|
|
4775
|
-
|
|
4776
|
-
|
|
4777
|
-
|
|
4778
|
-
|
|
4779
|
-
|
|
4780
|
-
|
|
4781
|
-
|
|
4782
|
-
|
|
4783
|
-
|
|
4784
|
-
|
|
4785
|
-
|
|
4786
|
-
|
|
4787
|
-
|
|
4788
|
-
|
|
4789
|
-
|
|
4790
|
-
|
|
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
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
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 =
|
|
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
|
|
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(
|
|
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
|
|
10081
|
-
|
|
10082
|
-
|
|
10083
|
-
|
|
10084
|
-
|
|
10085
|
-
|
|
10086
|
-
|
|
10087
|
-
|
|
10088
|
-
|
|
10089
|
-
|
|
10090
|
-
);
|
|
10091
|
-
|
|
10092
|
-
|
|
10093
|
-
|
|
10094
|
-
|
|
10095
|
-
|
|
10096
|
-
|
|
10097
|
-
|
|
10098
|
-
|
|
10099
|
-
|
|
10100
|
-
|
|
10101
|
-
|
|
10102
|
-
|
|
10103
|
-
|
|
10104
|
-
)
|
|
10105
|
-
|
|
10106
|
-
|
|
10107
|
-
|
|
10108
|
-
|
|
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
|
-
|
|
10111
|
-
|
|
10112
|
-
|
|
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
|
-
}, [
|
|
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 (!
|
|
11283
|
-
|
|
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
|
|
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
|
|
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-
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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(
|
|
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
|
|
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 } =
|
|
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
|
|
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 } =
|
|
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
|
|
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
|
-
} =
|
|
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
|
-
} =
|
|
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
|
|
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 } =
|
|
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
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
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 } =
|
|
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 =
|
|
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
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|
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 } =
|
|
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
|
-
|
|
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 =
|
|
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:
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
23307
|
+
const wallets = useMemo14(() => {
|
|
22589
23308
|
if (!paymentIntent) return [];
|
|
22590
23309
|
return mapDepositAddressesToWallets(paymentIntent.deposit_addresses, paymentIntent);
|
|
22591
23310
|
}, [paymentIntent]);
|
|
22592
|
-
const formatCryptoAmount =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
24834
|
+
const exchangeRate = useMemo16(() => {
|
|
24116
24835
|
if (!balanceData?.exchangeRate) return 0;
|
|
24117
24836
|
return parseFloat(balanceData.exchangeRate);
|
|
24118
24837
|
}, [balanceData]);
|
|
24119
|
-
const balanceCrypto =
|
|
24838
|
+
const balanceCrypto = useMemo16(() => {
|
|
24120
24839
|
if (!balanceData?.balanceHuman) return 0;
|
|
24121
24840
|
return parseFloat(balanceData.balanceHuman);
|
|
24122
24841
|
}, [balanceData]);
|
|
24123
|
-
const balanceUsdNum =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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,
|