@b3dotfun/sdk 0.0.79-alpha.1 → 0.0.79-alpha.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/cjs/anyspend/react/components/AnySpendCollectorClubPurchase.js +8 -1
- package/dist/cjs/anyspend/react/components/AnySpendCustom.d.ts +2 -0
- package/dist/cjs/anyspend/react/components/AnySpendCustom.js +53 -13
- package/dist/cjs/anyspend/react/components/AnySpendNFT.js +6 -9
- package/dist/cjs/anyspend/react/components/common/CryptoReceiveSection.js +1 -3
- package/dist/cjs/anyspend/react/components/common/FiatPaymentMethod.d.ts +2 -1
- package/dist/cjs/anyspend/react/components/common/FiatPaymentMethod.js +32 -12
- package/dist/cjs/anyspend/react/components/common/PanelOnramp.js +6 -6
- package/dist/cjs/anyspend/react/components/common/PanelOnrampPayment.js +5 -3
- package/dist/cjs/anyspend/react/hooks/useGeoOnrampOptions.d.ts +2 -8
- package/dist/cjs/anyspend/react/hooks/useGeoOnrampOptions.js +4 -2
- package/dist/cjs/anyspend/react/hooks/useStripeSupport.d.ts +1 -0
- package/dist/cjs/anyspend/react/hooks/useStripeSupport.js +1 -0
- package/dist/cjs/global-account/react/components/IPFSMediaRenderer/IPFSMediaRenderer.js +5 -3
- package/dist/cjs/shared/utils/ipfs.d.ts +3 -3
- package/dist/cjs/shared/utils/ipfs.js +8 -8
- package/dist/esm/anyspend/react/components/AnySpendCollectorClubPurchase.js +8 -1
- package/dist/esm/anyspend/react/components/AnySpendCustom.d.ts +2 -0
- package/dist/esm/anyspend/react/components/AnySpendCustom.js +53 -13
- package/dist/esm/anyspend/react/components/AnySpendNFT.js +7 -10
- package/dist/esm/anyspend/react/components/common/CryptoReceiveSection.js +1 -3
- package/dist/esm/anyspend/react/components/common/FiatPaymentMethod.d.ts +2 -1
- package/dist/esm/anyspend/react/components/common/FiatPaymentMethod.js +32 -12
- package/dist/esm/anyspend/react/components/common/PanelOnramp.js +6 -6
- package/dist/esm/anyspend/react/components/common/PanelOnrampPayment.js +5 -3
- package/dist/esm/anyspend/react/hooks/useGeoOnrampOptions.d.ts +2 -8
- package/dist/esm/anyspend/react/hooks/useGeoOnrampOptions.js +4 -2
- package/dist/esm/anyspend/react/hooks/useStripeSupport.d.ts +1 -0
- package/dist/esm/anyspend/react/hooks/useStripeSupport.js +1 -0
- package/dist/esm/global-account/react/components/IPFSMediaRenderer/IPFSMediaRenderer.js +5 -3
- package/dist/esm/shared/utils/ipfs.d.ts +3 -3
- package/dist/esm/shared/utils/ipfs.js +8 -8
- package/dist/styles/index.css +1 -1
- package/dist/types/anyspend/react/components/AnySpendCustom.d.ts +2 -0
- package/dist/types/anyspend/react/components/common/FiatPaymentMethod.d.ts +2 -1
- package/dist/types/anyspend/react/hooks/useGeoOnrampOptions.d.ts +2 -8
- package/dist/types/anyspend/react/hooks/useStripeSupport.d.ts +1 -0
- package/dist/types/shared/utils/ipfs.d.ts +3 -3
- package/package.json +1 -1
- package/src/anyspend/react/components/AnySpendCollectorClubPurchase.tsx +8 -0
- package/src/anyspend/react/components/AnySpendCustom.tsx +71 -26
- package/src/anyspend/react/components/AnySpendNFT.tsx +49 -48
- package/src/anyspend/react/components/common/CryptoReceiveSection.tsx +1 -4
- package/src/anyspend/react/components/common/FiatPaymentMethod.tsx +45 -21
- package/src/anyspend/react/components/common/PanelOnramp.tsx +13 -13
- package/src/anyspend/react/components/common/PanelOnrampPayment.tsx +53 -16
- package/src/anyspend/react/hooks/useGeoOnrampOptions.ts +5 -2
- package/src/anyspend/react/hooks/useStripeSupport.ts +1 -0
- package/src/global-account/react/components/IPFSMediaRenderer/IPFSMediaRenderer.tsx +5 -7
- package/src/shared/utils/ipfs.ts +8 -8
|
@@ -26,6 +26,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
26
26
|
* ```
|
|
27
27
|
*/
|
|
28
28
|
import { USDC_BASE } from "../../../anyspend/constants/index.js";
|
|
29
|
+
import { formatUnits } from "../../../shared/utils/number.js";
|
|
29
30
|
import { useMemo } from "react";
|
|
30
31
|
import { encodeFunctionData } from "viem";
|
|
31
32
|
import { AnySpendCustom } from "./AnySpendCustom.js";
|
|
@@ -55,6 +56,12 @@ export function AnySpendCollectorClubPurchase({ loadOrder, mode = "modal", activ
|
|
|
55
56
|
return "0";
|
|
56
57
|
}
|
|
57
58
|
}, [pricePerPack, packAmount]);
|
|
59
|
+
// Calculate fiat amount (totalAmount in USD, assuming USDC with 6 decimals)
|
|
60
|
+
const srcFiatAmount = useMemo(() => {
|
|
61
|
+
if (!totalAmount || totalAmount === "0")
|
|
62
|
+
return "0";
|
|
63
|
+
return formatUnits(totalAmount, USDC_BASE.decimals);
|
|
64
|
+
}, [totalAmount]);
|
|
58
65
|
// Encode the buyPacksFor function call
|
|
59
66
|
const encodedData = useMemo(() => {
|
|
60
67
|
try {
|
|
@@ -77,5 +84,5 @@ export function AnySpendCollectorClubPurchase({ loadOrder, mode = "modal", activ
|
|
|
77
84
|
pricePerPack,
|
|
78
85
|
vendingMachineId,
|
|
79
86
|
packType,
|
|
80
|
-
}, header: header || defaultHeader, onSuccess: onSuccess, showRecipient: showRecipient }));
|
|
87
|
+
}, header: header || defaultHeader, onSuccess: onSuccess, showRecipient: showRecipient, srcFiatAmount: srcFiatAmount }));
|
|
81
88
|
}
|
|
@@ -21,4 +21,6 @@ export declare function AnySpendCustom(props: {
|
|
|
21
21
|
onSuccess?: (txHash?: string) => void;
|
|
22
22
|
showRecipient?: boolean;
|
|
23
23
|
onShowPointsDetail?: () => void;
|
|
24
|
+
/** Fiat amount in USD for fiat payments */
|
|
25
|
+
srcFiatAmount?: string;
|
|
24
26
|
}): import("react/jsx-runtime").JSX.Element;
|
|
@@ -13,7 +13,6 @@ import { ChevronRight, ChevronRightCircle, Info, Loader2 } from "lucide-react";
|
|
|
13
13
|
import { motion } from "motion/react";
|
|
14
14
|
import React, { useCallback, useEffect, useMemo, useState } from "react";
|
|
15
15
|
import { base } from "viem/chains";
|
|
16
|
-
import { useFeatureFlags } from "../contexts/FeatureFlagsContext.js";
|
|
17
16
|
import { useAutoSetActiveWalletFromWagmi } from "../hooks/useAutoSetActiveWalletFromWagmi.js";
|
|
18
17
|
import { useCryptoPaymentMethodState } from "../hooks/useCryptoPaymentMethodState.js";
|
|
19
18
|
import { useRecipientAddressState } from "../hooks/useRecipientAddressState.js";
|
|
@@ -108,9 +107,8 @@ export function AnySpendCustom(props) {
|
|
|
108
107
|
const fingerprintConfig = getFingerprintConfig();
|
|
109
108
|
return (_jsx(AnySpendFingerprintWrapper, { fingerprint: fingerprintConfig, children: _jsx(AnySpendCustomInner, { ...props }) }));
|
|
110
109
|
}
|
|
111
|
-
function AnySpendCustomInner({ loadOrder, mode = "modal", activeTab: activeTabProps = "crypto", recipientAddress: recipientAddressProps, spenderAddress, orderType, dstChainId, dstToken, dstAmount, contractAddress, encodedData, metadata, header, onSuccess, showRecipient = true, onShowPointsDetail, }) {
|
|
110
|
+
function AnySpendCustomInner({ loadOrder, mode = "modal", activeTab: activeTabProps = "crypto", recipientAddress: recipientAddressProps, spenderAddress, orderType, dstChainId, dstToken, dstAmount, contractAddress, encodedData, metadata, header, onSuccess, showRecipient = true, onShowPointsDetail, srcFiatAmount: srcFiatAmountProps, }) {
|
|
112
111
|
const hasMounted = useHasMounted();
|
|
113
|
-
const featureFlags = useFeatureFlags();
|
|
114
112
|
const searchParams = useSearchParamsSSR();
|
|
115
113
|
const router = useRouter();
|
|
116
114
|
// Auto-set active wallet from wagmi
|
|
@@ -243,9 +241,29 @@ function AnySpendCustomInner({ loadOrder, mode = "modal", activeTab: activeTabPr
|
|
|
243
241
|
}
|
|
244
242
|
}, [activeTab, anyspendQuote?.data]);
|
|
245
243
|
const formattedSrcAmount = srcAmount ? formatTokenAmount(srcAmount, srcToken.decimals, 6, false) : null;
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
244
|
+
// Calculate fiat amount for geo check (regardless of activeTab) to determine tab availability
|
|
245
|
+
const srcFiatAmountForGeoCheck = useMemo(() => {
|
|
246
|
+
// Use prop if provided
|
|
247
|
+
if (srcFiatAmountProps) {
|
|
248
|
+
return srcFiatAmountProps;
|
|
249
|
+
}
|
|
250
|
+
// Fallback to dstAmount if destination token is USDC
|
|
251
|
+
if (dstAmount && dstToken.address.toLowerCase() === USDC_BASE.address.toLowerCase()) {
|
|
252
|
+
return formatUnits(dstAmount, USDC_BASE.decimals);
|
|
253
|
+
}
|
|
254
|
+
// Use srcAmount if available (from quote)
|
|
255
|
+
if (srcAmount) {
|
|
256
|
+
return formatUnits(srcAmount.toString(), USDC_BASE.decimals);
|
|
257
|
+
}
|
|
258
|
+
return "0";
|
|
259
|
+
}, [srcAmount, srcFiatAmountProps, dstAmount, dstToken.address]);
|
|
260
|
+
const srcFiatAmount = useMemo(() => {
|
|
261
|
+
if (activeTab !== "fiat")
|
|
262
|
+
return "0";
|
|
263
|
+
return srcFiatAmountForGeoCheck;
|
|
264
|
+
}, [activeTab, srcFiatAmountForGeoCheck]);
|
|
265
|
+
// Get geo data and onramp options (use srcFiatAmountForGeoCheck to check availability regardless of activeTab)
|
|
266
|
+
const { geoData, isOnrampSupported, coinbaseAvailablePaymentMethods, stripeOnrampSupport, stripeWeb2Support } = useGeoOnrampOptions(srcFiatAmountForGeoCheck);
|
|
249
267
|
useEffect(() => {
|
|
250
268
|
if (oat?.data?.order.status === "executed" && !onSuccessCalled.current) {
|
|
251
269
|
console.log("Calling onSuccess");
|
|
@@ -340,9 +358,13 @@ function AnySpendCustomInner({ loadOrder, mode = "modal", activeTab: activeTabPr
|
|
|
340
358
|
invariant((activeTab === "fiat" ? base.id : srcChainId) === base.id, "Selected src chain is not base");
|
|
341
359
|
// Get the current geo data from the hook
|
|
342
360
|
const currentGeoData = geoData;
|
|
361
|
+
// Use total amount from quote (includes fees) for onramp, fallback to srcFiatAmount
|
|
362
|
+
const onrampAmount = anyspendQuote?.data?.currencyIn?.amountUsd
|
|
363
|
+
? anyspendQuote.data.currencyIn.amountUsd.toString()
|
|
364
|
+
: srcFiatAmount;
|
|
343
365
|
void createOnrampOrder({
|
|
344
366
|
...createOrderParams,
|
|
345
|
-
srcFiatAmount:
|
|
367
|
+
srcFiatAmount: onrampAmount,
|
|
346
368
|
onramp: {
|
|
347
369
|
vendor: onramp.vendor,
|
|
348
370
|
paymentMethod: onramp.paymentMethod,
|
|
@@ -412,12 +434,22 @@ function AnySpendCustomInner({ loadOrder, mode = "modal", activeTab: activeTabPr
|
|
|
412
434
|
vendor = "coinbase";
|
|
413
435
|
paymentMethodString = coinbaseAvailablePaymentMethods[0]?.id || "";
|
|
414
436
|
}
|
|
415
|
-
else if (paymentMethod === FiatPaymentMethod.
|
|
437
|
+
else if (paymentMethod === FiatPaymentMethod.STRIPE_WEB2) {
|
|
438
|
+
// Stripe Web2 embedded payment
|
|
416
439
|
if (!stripeWeb2Support || !stripeWeb2Support.isSupport) {
|
|
417
|
-
toast.error("Stripe not available");
|
|
440
|
+
toast.error("Stripe embedded payment not available");
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
vendor = "stripe-web2";
|
|
444
|
+
paymentMethodString = "";
|
|
445
|
+
}
|
|
446
|
+
else if (paymentMethod === FiatPaymentMethod.STRIPE) {
|
|
447
|
+
// Stripe redirect (one-click buy URL)
|
|
448
|
+
if (!stripeOnrampSupport) {
|
|
449
|
+
toast.error("Stripe redirect payment not available");
|
|
418
450
|
return;
|
|
419
451
|
}
|
|
420
|
-
vendor =
|
|
452
|
+
vendor = "stripe";
|
|
421
453
|
paymentMethodString = "";
|
|
422
454
|
}
|
|
423
455
|
else {
|
|
@@ -464,7 +496,7 @@ function AnySpendCustomInner({ loadOrder, mode = "modal", activeTab: activeTabPr
|
|
|
464
496
|
const loadingView = (_jsxs("div", { className: cn("mx-auto flex w-full flex-col items-center gap-4 p-5", mode === "modal" && "bg-b3-react-background"), children: [_jsxs(Badge, { variant: "default", className: "bg-b3-react-muted/30 border-b3-react-border hover:bg-b3-react-muted/50 flex items-center gap-3 px-4 py-1 text-base transition-all", children: [_jsx(Loader2, { className: "text-b3-react-foreground size-4 animate-spin" }), _jsx(TextShimmer, { duration: 1, className: "font-sf-rounded text-base font-semibold", children: "Loading..." })] }), _jsxs("div", { className: "flex w-full flex-1 flex-col", children: [_jsxs("div", { className: "mb-4 flex flex-col gap-1", children: [_jsx(Skeleton, { className: "h-4 w-24" }), _jsxs("div", { className: "mt-2 flex items-center gap-2", children: [_jsx(Skeleton, { className: "h-8 w-48" }), _jsx(Skeleton, { className: "ml-4 h-8 w-32" })] }), _jsx(Skeleton, { className: "mt-4 h-8 w-24" })] }), _jsx(Skeleton, { className: "mb-4 h-12 w-full" }), _jsxs("div", { className: "flex w-full items-center justify-between gap-4", children: [_jsxs(Skeleton, { className: "rounded-lg bg-white/5 p-6 pb-3", children: [_jsx("div", { className: "size-[200px]" }), _jsx("div", { className: "mt-3 flex items-center justify-center gap-2", children: _jsx("div", { className: "size-5 rounded-full" }) })] }), _jsx("div", { className: "flex flex-1 flex-col gap-2", children: [1, 2, 3].map(i => (_jsx(Skeleton, { className: "h-10 w-full" }, i))) })] })] }), _jsxs("div", { className: "bg-b3-react-muted/30 mt-8 w-full rounded-lg p-4", children: [_jsx(Skeleton, { className: "mb-3 h-4 w-48" }), _jsxs("div", { className: "flex items-center gap-4", children: [_jsx(Skeleton, { className: "h-10 flex-1" }), _jsx(Skeleton, { className: "h-10 flex-1" })] })] }), _jsx("div", { className: "flex w-full flex-col gap-3", children: [1, 2, 3, 4, 5].map(i => (_jsxs("div", { className: "flex w-full justify-between", children: [_jsx(Skeleton, { className: "h-4 w-24" }), _jsx(Skeleton, { className: "h-4 w-32" })] }, i))) }), _jsx(Skeleton, { className: "h-10 w-full" }), mode === "page" && _jsx("div", { className: "h-12" })] }));
|
|
465
497
|
// Render points badge if conditions are met
|
|
466
498
|
const renderPointsBadge = () => {
|
|
467
|
-
if (
|
|
499
|
+
if (anyspendQuote?.data?.pointsAmount && anyspendQuote.data.pointsAmount > 0) {
|
|
468
500
|
return (_jsx(PointsBadge, { pointsAmount: anyspendQuote.data.pointsAmount, pointsMultiplier: anyspendQuote.data.pointsMultiplier, onClick: () => {
|
|
469
501
|
onShowPointsDetail?.();
|
|
470
502
|
setActivePanel(PanelView.POINTS_DETAIL);
|
|
@@ -511,8 +543,16 @@ function AnySpendCustomInner({ loadOrder, mode = "modal", activeTab: activeTabPr
|
|
|
511
543
|
opacity: hasMounted ? 1 : 0,
|
|
512
544
|
y: hasMounted ? 0 : 20,
|
|
513
545
|
filter: hasMounted ? "blur(0px)" : "blur(10px)",
|
|
514
|
-
}, transition: { duration: 0.3, delay: 0.1, ease: "easeInOut" }, className: "relative flex w-full items-center justify-between gap-4", children: [_jsxs("span", { className: "text-as-tertiarry flex flex-wrap items-center gap-1.5 text-sm", children: [_jsxs("span", { className: "whitespace-nowrap", children: ["Total ", _jsx("span", { className: "text-as-tertiarry", children: "(USD)" })] }), anyspendQuote?.data?.fee && (_jsx(TooltipProvider, { children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { className: "text-as-primary/40 hover:text-as-primary/60 transition-colors", children: _jsx(Info, { className: "h-4 w-4" }) }) }), _jsx(TooltipContent, { side: "top", children: _jsx(FeeBreakDown, { fee: anyspendQuote.data.fee }) })] }) }))] }), _jsxs("div", { className: "flex flex-col items-end gap-0.5", children: [_jsxs("div", { className: "flex flex-wrap items-center justify-end gap-2", children: [renderPointsBadge(),
|
|
515
|
-
|
|
546
|
+
}, transition: { duration: 0.3, delay: 0.1, ease: "easeInOut" }, className: "relative flex w-full items-center justify-between gap-4", children: [_jsxs("span", { className: "text-as-tertiarry flex flex-wrap items-center gap-1.5 text-sm", children: [_jsxs("span", { className: "whitespace-nowrap", children: ["Total ", _jsx("span", { className: "text-as-tertiarry", children: "(USD)" })] }), anyspendQuote?.data?.fee && (_jsx(TooltipProvider, { children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { className: "text-as-primary/40 hover:text-as-primary/60 transition-colors", children: _jsx(Info, { className: "h-4 w-4" }) }) }), _jsx(TooltipContent, { side: "top", children: _jsx(FeeBreakDown, { fee: anyspendQuote.data.fee }) })] }) }))] }), _jsxs("div", { className: "flex flex-col items-end gap-0.5", children: [_jsxs("div", { className: "flex flex-wrap items-center justify-end gap-2", children: [renderPointsBadge(), isLoadingAnyspendQuote ? (_jsx("div", { className: "bg-as-surface-secondary h-7 w-16 animate-pulse rounded" })) : (_jsx("span", { className: "text-as-primary whitespace-nowrap text-xl font-semibold", children: anyspendQuote?.data?.currencyIn?.amountUsd ? (`$${Number(anyspendQuote.data.currencyIn.amountUsd).toFixed(2)}`) : (_jsxs(_Fragment, { children: ["$", parseFloat(srcFiatAmount || "0").toFixed(2), _jsx("span", { className: "text-as-tertiarry text-base", children: "+" })] })) }))] }), (() => {
|
|
547
|
+
if (anyspendQuote?.data?.fee?.type === "stripeweb2_fee" && anyspendQuote.data.fee.originalAmount) {
|
|
548
|
+
const fee = (Number(anyspendQuote.data.fee.originalAmount) - Number(anyspendQuote.data.fee.finalAmount)) /
|
|
549
|
+
1e6;
|
|
550
|
+
if (fee > 0) {
|
|
551
|
+
return _jsxs("span", { className: "text-as-secondary text-xs", children: ["incl. $", fee.toFixed(2), " fee"] });
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
return null;
|
|
555
|
+
})()] })] })] }), _jsx("div", { className: cn("flex w-full flex-col items-center justify-between gap-2"), children: _jsx(motion.div, { initial: false, animate: {
|
|
516
556
|
opacity: hasMounted ? 1 : 0,
|
|
517
557
|
y: hasMounted ? 0 : 20,
|
|
518
558
|
filter: hasMounted ? "blur(0px)" : "blur(10px)",
|
|
@@ -6,7 +6,7 @@ import { getIpfsUrl } from "../../../shared/utils/ipfs.js";
|
|
|
6
6
|
import { formatDisplayNumber, formatTokenAmount } from "../../../shared/utils/number.js";
|
|
7
7
|
import { MoreVertical } from "lucide-react";
|
|
8
8
|
import { AnimatePresence } from "motion/react";
|
|
9
|
-
import { useEffect, useState } from "react";
|
|
9
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
10
10
|
import { b3 } from "viem/chains";
|
|
11
11
|
import { AnySpendCustom } from "./AnySpendCustom.js";
|
|
12
12
|
// ABI for contractURI and uri functions
|
|
@@ -28,16 +28,16 @@ const CONTRACT_URI_ABI = [
|
|
|
28
28
|
];
|
|
29
29
|
export function AnySpendNFT({ loadOrder, mode = "modal", recipientAddress, nftContract, onSuccess, onShowPointsDetail, }) {
|
|
30
30
|
const [imageUrlWithFallback, setFallbackImageUrl] = useState(nftContract.imageUrl);
|
|
31
|
-
const
|
|
31
|
+
const hasFetchedRef = useRef(false);
|
|
32
32
|
// Fetch contract metadata when imageUrl is empty
|
|
33
33
|
useEffect(() => {
|
|
34
34
|
async function fetchContractMetadata() {
|
|
35
35
|
// fetch image Uri if not provided
|
|
36
|
-
if (nftContract.imageUrl ||
|
|
36
|
+
if (nftContract.imageUrl || hasFetchedRef.current) {
|
|
37
37
|
return;
|
|
38
38
|
}
|
|
39
|
+
hasFetchedRef.current = true;
|
|
39
40
|
try {
|
|
40
|
-
setIsLoadingFallback(true);
|
|
41
41
|
// Use the chainIdToPublicClient utility function
|
|
42
42
|
const publicClient = chainIdToPublicClient(nftContract.chainId);
|
|
43
43
|
let metadataURI;
|
|
@@ -74,15 +74,12 @@ export function AnySpendNFT({ loadOrder, mode = "modal", recipientAddress, nftCo
|
|
|
74
74
|
catch (error) {
|
|
75
75
|
console.error("Error fetching contract metadata:", error);
|
|
76
76
|
}
|
|
77
|
-
finally {
|
|
78
|
-
setIsLoadingFallback(false);
|
|
79
|
-
}
|
|
80
77
|
}
|
|
81
78
|
fetchContractMetadata();
|
|
82
|
-
}, [nftContract.contractAddress, nftContract.chainId, nftContract.imageUrl, nftContract.tokenId
|
|
83
|
-
const header = ({ anyspendPrice, isLoadingAnyspendPrice, }) => (_jsxs(_Fragment, { children: [_jsxs("div", { className: "relative size-[200px]", children: [_jsx("div", { className: "absolute inset-0 scale-95 bg-black/30 blur-md" }), _jsxs(GlareCard, { className: "overflow-hidden", children: [imageUrlWithFallback && (_jsx("img", { src: imageUrlWithFallback, alt: nftContract.name, className: "size-full object-cover" })), _jsx("div", { className: "absolute inset-0 rounded-xl border border-white/10" })] }), _jsx(DropdownMenu, { nftContract: nftContract })] }), _jsxs("div", { className: "from-b3-react-background to-as-on-surface-1 -mb-5 mt-[-100px] w-full rounded-t-lg bg-gradient-to-t", children: [_jsx("div", { className: "h-[100px] w-full" }), _jsxs("div", { className: "mb-1 flex w-full flex-col items-center gap-2 p-5", children: [_jsx("span", { className: "font-sf-rounded text-2xl font-semibold", children: nftContract.name }), _jsx("div", { className: "flex w-fit items-center gap-1", children: anyspendPrice ? (_jsx(AnimatePresence, { mode: "wait", children: _jsx("div", { className: cn("text-as-primary group flex items-center text-3xl font-semibold transition-all", {
|
|
79
|
+
}, [nftContract.contractAddress, nftContract.chainId, nftContract.imageUrl, nftContract.tokenId]);
|
|
80
|
+
const header = useCallback(({ anyspendPrice, isLoadingAnyspendPrice, }) => (_jsxs(_Fragment, { children: [_jsxs("div", { className: "relative size-[200px]", children: [_jsx("div", { className: "absolute inset-0 scale-95 bg-black/30 blur-md" }), _jsxs(GlareCard, { className: "overflow-hidden", children: [imageUrlWithFallback && (_jsx("img", { src: imageUrlWithFallback, alt: nftContract.name, className: "size-full object-cover" })), _jsx("div", { className: "absolute inset-0 rounded-xl border border-white/10" })] }), _jsx(DropdownMenu, { nftContract: nftContract })] }), _jsxs("div", { className: "from-b3-react-background to-as-on-surface-1 -mb-5 mt-[-100px] w-full rounded-t-lg bg-gradient-to-t", children: [_jsx("div", { className: "h-[100px] w-full" }), _jsxs("div", { className: "mb-1 flex w-full flex-col items-center gap-2 p-5", children: [_jsx("span", { className: "font-sf-rounded text-2xl font-semibold", children: nftContract.name }), _jsx("div", { className: "flex w-fit items-center gap-1", children: anyspendPrice ? (_jsx(AnimatePresence, { mode: "wait", children: _jsx("div", { className: cn("text-as-primary group flex items-center text-3xl font-semibold transition-all", {
|
|
84
81
|
"opacity-0": isLoadingAnyspendPrice,
|
|
85
|
-
}), children: formatDisplayNumber(anyspendPrice?.data?.currencyIn?.amountUsd, { style: "currency" }) }) })) : (_jsx("div", { className: "h-[36px] w-full" })) })] })] })] }));
|
|
82
|
+
}), children: formatDisplayNumber(anyspendPrice?.data?.currencyIn?.amountUsd, { style: "currency" }) }) })) : (_jsx("div", { className: "h-[36px] w-full" })) })] })] })] })), [imageUrlWithFallback, nftContract]);
|
|
86
83
|
return (_jsx(AnySpendCustom, { loadOrder: loadOrder, mode: mode, activeTab: "fiat", recipientAddress: recipientAddress, orderType: "mint_nft", dstChainId: nftContract.chainId, dstToken: nftContract.currency, dstAmount: nftContract.price, contractAddress: nftContract.contractAddress, encodedData: "0x", metadata: {
|
|
87
84
|
type: "mint_nft",
|
|
88
85
|
nftContract: nftContract,
|
|
@@ -5,11 +5,9 @@ import { shortenAddress } from "../../../../shared/utils/formatAddress.js";
|
|
|
5
5
|
import { formatDisplayNumber } from "../../../../shared/utils/number.js";
|
|
6
6
|
import { ChevronRight, Info } from "lucide-react";
|
|
7
7
|
import { motion } from "motion/react";
|
|
8
|
-
import { useFeatureFlags } from "../../contexts/FeatureFlagsContext.js";
|
|
9
8
|
import { OrderTokenAmount } from "./OrderTokenAmount.js";
|
|
10
9
|
import { PointsBadge } from "./PointsBadge.js";
|
|
11
10
|
export function CryptoReceiveSection({ isDepositMode = false, isBuyMode = false, effectiveRecipientAddress, recipientName, onSelectRecipient, dstAmount, dstToken, selectedDstChainId, setSelectedDstChainId, setSelectedDstToken, isSrcInputDirty, onChangeDstAmount, anyspendQuote, dstTokenSymbol, dstTokenLogoURI, onShowPointsDetail, onShowFeeDetail, }) {
|
|
12
|
-
const featureFlags = useFeatureFlags();
|
|
13
11
|
return (_jsxs(motion.div, { initial: { opacity: 0, y: 20, filter: "blur(10px)" }, animate: { opacity: 1, y: 0, filter: "blur(0px)" }, transition: { duration: 0.3, delay: 0.1, ease: "easeInOut" }, className: "receive-section bg-as-surface-secondary border-as-border-secondary relative flex w-full flex-col gap-2 rounded-2xl border p-4 sm:p-6", children: [_jsxs("div", { className: "flex w-full items-center justify-between", children: [_jsxs("div", { className: "text-as-primary/50 flex h-7 items-center gap-1.5 text-sm", children: [isDepositMode ? "Deposit" : "Receive", isSrcInputDirty && anyspendQuote?.data?.fee && onShowFeeDetail && (_jsx("button", { onClick: onShowFeeDetail, className: "text-as-primary/40 hover:text-as-primary/60 transition-colors", children: _jsx(Info, { className: "h-4 w-4" }) }))] }), effectiveRecipientAddress ? (_jsx("button", { className: cn("text-as-tertiarry flex h-7 items-center gap-2 rounded-lg"), onClick: onSelectRecipient, children: _jsxs(_Fragment, { children: [_jsx("span", { className: "text-as-tertiarry flex items-center gap-1 text-sm", children: recipientName ? formatUsername(recipientName) : shortenAddress(effectiveRecipientAddress || "") }), _jsx(ChevronRight, { className: "h-4 w-4" })] }) })) : (_jsx("button", { className: "text-as-primary/70 flex items-center gap-1 rounded-lg", onClick: onSelectRecipient, children: _jsx("div", { className: "text-sm font-medium", children: "Select recipient" }) }))] }), isBuyMode || isDepositMode ? (
|
|
14
12
|
// Fixed destination token display for buy mode and deposit mode
|
|
15
13
|
_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("div", { className: "text-as-primary text-2xl font-bold", children: dstAmount || "0" }), _jsxs("div", { className: "bg-as-brand/10 border-as-brand/30 flex items-center gap-3 rounded-xl border px-4 py-3", children: [(dstTokenLogoURI || dstToken.metadata?.logoURI) && (_jsx("img", { src: dstTokenLogoURI || dstToken.metadata?.logoURI, alt: dstTokenSymbol || dstToken.symbol, className: "h-8 w-8 rounded-full" })), _jsx("span", { className: "text-as-brand text-lg font-bold", children: dstTokenSymbol || dstToken.symbol })] })] })) : (
|
|
@@ -55,5 +53,5 @@ export function CryptoReceiveSection({ isDepositMode = false, isBuyMode = false,
|
|
|
55
53
|
}
|
|
56
54
|
// Using inline style to ensure color displays
|
|
57
55
|
return (_jsxs("span", { className: "ml-2", style: { color: actualSlippage >= redThreshold ? "red" : "#FFD700" }, children: ["(", isNegative ? "-" : "", percentage, "%)"] }));
|
|
58
|
-
})()] }),
|
|
56
|
+
})()] }), anyspendQuote?.data?.pointsAmount > 0 && (_jsx(PointsBadge, { pointsAmount: anyspendQuote.data.pointsAmount, pointsMultiplier: anyspendQuote.data.pointsMultiplier, onClick: () => onShowPointsDetail?.() }))] })] }));
|
|
59
57
|
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
export declare enum FiatPaymentMethod {
|
|
2
2
|
NONE = "none",
|
|
3
3
|
COINBASE_PAY = "coinbase_pay",
|
|
4
|
-
STRIPE = "stripe"
|
|
4
|
+
STRIPE = "stripe",// Stripe redirect (one-click buy URL)
|
|
5
|
+
STRIPE_WEB2 = "stripe_web2"
|
|
5
6
|
}
|
|
6
7
|
interface FiatPaymentMethodProps {
|
|
7
8
|
selectedPaymentMethod: FiatPaymentMethod;
|
|
@@ -8,26 +8,30 @@ export var FiatPaymentMethod;
|
|
|
8
8
|
FiatPaymentMethod["NONE"] = "none";
|
|
9
9
|
FiatPaymentMethod["COINBASE_PAY"] = "coinbase_pay";
|
|
10
10
|
FiatPaymentMethod["STRIPE"] = "stripe";
|
|
11
|
+
FiatPaymentMethod["STRIPE_WEB2"] = "stripe_web2";
|
|
11
12
|
})(FiatPaymentMethod || (FiatPaymentMethod = {}));
|
|
12
13
|
export function FiatPaymentMethodComponent({ selectedPaymentMethod, setSelectedPaymentMethod, onBack, onSelectPaymentMethod, srcAmountOnRamp, }) {
|
|
14
|
+
// Load geo-based onramp options like in PanelOnramp
|
|
15
|
+
const { coinbaseAvailablePaymentMethods, stripeOnrampSupport, stripeWeb2Support, isLoading: isLoadingGeoOnramp, } = useGeoOnrampOptions(srcAmountOnRamp);
|
|
13
16
|
// Helper function to get fees from API data
|
|
14
17
|
const getFeeFromApi = (paymentMethod) => {
|
|
15
18
|
switch (paymentMethod) {
|
|
16
19
|
case FiatPaymentMethod.COINBASE_PAY:
|
|
17
20
|
// Coinbase doesn't provide fee info in API, return null
|
|
18
21
|
return null;
|
|
19
|
-
case FiatPaymentMethod.
|
|
20
|
-
// Get fee from Stripe API response
|
|
22
|
+
case FiatPaymentMethod.STRIPE_WEB2:
|
|
23
|
+
// Get fee from Stripe Web2 API response
|
|
21
24
|
if (stripeWeb2Support && "formattedFeeUsd" in stripeWeb2Support) {
|
|
22
25
|
return stripeWeb2Support.formattedFeeUsd;
|
|
23
26
|
}
|
|
24
27
|
return null;
|
|
28
|
+
case FiatPaymentMethod.STRIPE:
|
|
29
|
+
// Stripe redirect doesn't have fee info from API
|
|
30
|
+
return null;
|
|
25
31
|
default:
|
|
26
32
|
return null; // No fee when no payment method selected
|
|
27
33
|
}
|
|
28
34
|
};
|
|
29
|
-
// Load geo-based onramp options like in PanelOnramp
|
|
30
|
-
const { coinbaseAvailablePaymentMethods, stripeWeb2Support, isLoading: isLoadingGeoOnramp, } = useGeoOnrampOptions(srcAmountOnRamp);
|
|
31
35
|
// Generate payment methods based on geo availability (like in PanelOnrampPayment)
|
|
32
36
|
const availablePaymentMethods = [];
|
|
33
37
|
// Add Coinbase Pay if available
|
|
@@ -42,15 +46,27 @@ export function FiatPaymentMethodComponent({ selectedPaymentMethod, setSelectedP
|
|
|
42
46
|
available: true,
|
|
43
47
|
});
|
|
44
48
|
}
|
|
45
|
-
// Add Stripe if available
|
|
46
|
-
if (
|
|
47
|
-
const stripeFee = getFeeFromApi(FiatPaymentMethod.
|
|
49
|
+
// Add Stripe redirect (one-click) if available - primary option
|
|
50
|
+
if (stripeOnrampSupport) {
|
|
51
|
+
const stripeFee = getFeeFromApi(FiatPaymentMethod.STRIPE_WEB2); // Use same fee estimate
|
|
48
52
|
availablePaymentMethods.push({
|
|
49
53
|
id: FiatPaymentMethod.STRIPE,
|
|
50
|
-
name: "
|
|
51
|
-
description: "
|
|
52
|
-
badge: stripeFee ? `$${Number(stripeFee).toFixed(2)} fee` :
|
|
53
|
-
badgeColor: "bg-
|
|
54
|
+
name: "Credit/Debit Card",
|
|
55
|
+
description: "Pay via Stripe checkout",
|
|
56
|
+
badge: stripeFee ? `$${Number(stripeFee).toFixed(2)} fee` : undefined,
|
|
57
|
+
badgeColor: "bg-gray-100 text-gray-800",
|
|
58
|
+
available: true,
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
// Add Stripe Web2 (embedded) if available - secondary option
|
|
62
|
+
if (stripeWeb2Support && stripeWeb2Support.isSupport) {
|
|
63
|
+
const stripeFee = getFeeFromApi(FiatPaymentMethod.STRIPE_WEB2);
|
|
64
|
+
availablePaymentMethods.push({
|
|
65
|
+
id: FiatPaymentMethod.STRIPE_WEB2,
|
|
66
|
+
name: "Quick Pay",
|
|
67
|
+
description: "Credit or debit card",
|
|
68
|
+
badge: stripeFee ? `$${Number(stripeFee).toFixed(2)} fee` : undefined,
|
|
69
|
+
badgeColor: "bg-gray-100 text-gray-800",
|
|
54
70
|
available: true,
|
|
55
71
|
});
|
|
56
72
|
}
|
|
@@ -63,5 +79,9 @@ export function FiatPaymentMethodComponent({ selectedPaymentMethod, setSelectedP
|
|
|
63
79
|
onSelectPaymentMethod(method.id);
|
|
64
80
|
}, className: cn("fiat-payment-method-item bg-as-surface-secondary border-as-border-secondary flex w-full items-center gap-4 rounded-2xl border p-4 transition-all duration-200", selectedPaymentMethod === method.id
|
|
65
81
|
? "border-as-brand bg-as-brand/10"
|
|
66
|
-
: "hover:border-as-brand/50 hover:bg-as-brand/5"), children: [_jsx("div", { className: "flex h-12 w-12 items-center justify-center rounded-full bg-blue-600 text-2xl text-white", children: method.id === FiatPaymentMethod.COINBASE_PAY
|
|
82
|
+
: "hover:border-as-brand/50 hover:bg-as-brand/5"), children: [_jsx("div", { className: "flex h-12 w-12 items-center justify-center rounded-full bg-blue-600 text-2xl text-white", children: method.id === FiatPaymentMethod.COINBASE_PAY
|
|
83
|
+
? "C"
|
|
84
|
+
: method.id === FiatPaymentMethod.STRIPE || method.id === FiatPaymentMethod.STRIPE_WEB2
|
|
85
|
+
? "S"
|
|
86
|
+
: "?" }), _jsxs("div", { className: "flex flex-1 flex-col items-start text-left", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: "text-as-primary text-base font-semibold", children: method.name }), method.badge && (_jsx("span", { className: cn("rounded-full px-2 py-1 text-xs font-medium", method.badgeColor), children: method.badge }))] }), _jsx("span", { className: "text-as-primary/60 text-sm", children: method.description })] }), selectedPaymentMethod === method.id && _jsx(ChevronRight, { className: "text-as-brand h-5 w-5" })] }, method.id)))) })] }) }));
|
|
67
87
|
}
|
|
@@ -6,13 +6,11 @@ import { cn, formatUsername } from "../../../../shared/utils/index.js";
|
|
|
6
6
|
import { formatAddress } from "../../../../shared/utils/formatAddress.js";
|
|
7
7
|
import { ChevronRight, Info, Wallet } from "lucide-react";
|
|
8
8
|
import { useRef } from "react";
|
|
9
|
-
import { useFeatureFlags } from "../../contexts/FeatureFlagsContext.js";
|
|
10
9
|
import { FiatPaymentMethod } from "./FiatPaymentMethod.js";
|
|
11
10
|
import { OrderTokenAmountFiat } from "./OrderTokenAmountFiat.js";
|
|
12
11
|
import { PointsBadge } from "./PointsBadge.js";
|
|
13
12
|
const ONE_CHAR_WIDTH = 30;
|
|
14
13
|
export function PanelOnramp({ srcAmountOnRamp, setSrcAmountOnRamp, selectedPaymentMethod, setActivePanel, _recipientAddress, destinationToken, destinationChainId, destinationAmount, onDestinationTokenChange, onDestinationChainChange, fiatPaymentMethodIndex, recipientSelectionPanelIndex, dstTokenSymbol, hideDstToken = false, anyspendQuote, onShowPointsDetail, onShowFeeDetail, customUsdInputValues = ["5", "10", "20", "25"], }) {
|
|
15
|
-
const featureFlags = useFeatureFlags();
|
|
16
14
|
// Helper function to get fees from anyspend quote
|
|
17
15
|
const getFeeFromApi = (paymentMethod) => {
|
|
18
16
|
// Try to get fee from anyspend quote first (most accurate)
|
|
@@ -38,10 +36,14 @@ export function PanelOnramp({ srcAmountOnRamp, setSrcAmountOnRamp, selectedPayme
|
|
|
38
36
|
// Helper function to get total amount from API (for Stripe) or calculate it (for others)
|
|
39
37
|
const getTotalAmount = (paymentMethod) => {
|
|
40
38
|
const baseAmount = parseFloat(srcAmountOnRamp) || 5;
|
|
41
|
-
//
|
|
39
|
+
// For stripeweb2_fee, use the originalAmount
|
|
42
40
|
if (anyspendQuote?.data?.fee?.type === "stripeweb2_fee") {
|
|
43
41
|
return Number(anyspendQuote.data.fee.originalAmount) / 1e6; // Convert from wei to USD
|
|
44
42
|
}
|
|
43
|
+
// Use currencyIn.amountUsd from quote when available (includes fees, most accurate for custom orders)
|
|
44
|
+
if (anyspendQuote?.data?.currencyIn?.amountUsd) {
|
|
45
|
+
return Number(anyspendQuote.data.currencyIn.amountUsd);
|
|
46
|
+
}
|
|
45
47
|
const fee = getFeeFromApi(paymentMethod);
|
|
46
48
|
// For Coinbase or when fee is available, calculate manually
|
|
47
49
|
if (fee !== null) {
|
|
@@ -79,9 +81,7 @@ export function PanelOnramp({ srcAmountOnRamp, setSrcAmountOnRamp, selectedPayme
|
|
|
79
81
|
.filter(v => !isNaN(Number(v)))
|
|
80
82
|
.map(value => (_jsxs("button", { onClick: () => handleQuickAmount(value), className: `bg-as-surface-secondary border-as-border-secondary hover:border-as-border-secondary h-7 w-14 rounded-lg border text-sm font-medium transition-all duration-200 ${srcAmountOnRamp === value
|
|
81
83
|
? "border-as-border-secondary bg-as-surface-secondary"
|
|
82
|
-
: "bg-as-surface-secondary hover:bg-as-surface-secondary"}`, children: ["$", value] }, value))) }), destinationToken && destinationChainId && !hideDstToken && (_jsx(OrderTokenAmountFiat, { address: _recipientAddress, context: "to", inputValue: destinationAmount || "0", onChangeInput: () => { }, chainId: destinationChainId, setChainId: onDestinationChainChange || (() => { }), token: destinationToken, setToken: onDestinationTokenChange || (() => { }) }))] }), _jsxs("div", { className: "border-as-border-secondary bg-as-surface-secondary mt-4 flex w-full flex-col gap-3 rounded-xl border p-4", children: [_jsxs("div", { className: "flex w-full items-center justify-between gap-2", children: [_jsx("span", { className: "text-as-tertiarry flex items-center text-sm", children: "Recipient" }), _recipientAddress ? (_jsxs("button", { className: "text-as-tertiarry flex h-7 items-center gap-1 text-sm transition-colors hover:text-blue-700", onClick: () => setActivePanel(recipientSelectionPanelIndex), children: [_jsx("span", { className: "text-sm", children: recipientName ? formatUsername(recipientName) : formatAddress(_recipientAddress) }), _jsx(ChevronRight, { size: 16 })] })) : (_jsxs("button", { className: "text-as-tertiarry flex h-7 items-center gap-1 text-sm transition-colors hover:text-blue-700", onClick: () => setActivePanel(5), children: [_jsx(Wallet, { className: "text-as-brand", size: 16 }), "Select recipient", _jsx(ChevronRight, { size: 16 })] }))] }), _jsx("div", { className: "divider w-full" }), _jsxs("div", { className: "flex items-center justify-between gap-4", children: [_jsx("span", { className: "text-as-tertiarry text-sm", children: "Expected to receive" }), _jsxs("div", { className: "flex flex-wrap items-center justify-end gap-1", children: [_jsxs("span", { className: "text-as-primary font-semibold", children: [destinationAmount || "0", " ", dstTokenSymbol || destinationToken?.symbol || ""] }), _jsxs("div", { className: "flex items-center gap-1", children: [_jsxs("span", { className: "text-as-tertiarry text-sm", children: ["on ", destinationChainId ? ALL_CHAINS[destinationChainId]?.name : ""] }), destinationToken && destinationChainId && destinationToken.metadata?.logoURI && (_jsx("img", { src: ALL_CHAINS[destinationChainId]?.logoUrl, alt: "Chain", className: "h-4 w-4 rounded-full" }))] })] })] }), _jsx("div", { className: "divider w-full" }), _jsx("div", { className: "", children: _jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { className: "flex items-center gap-1.5", children: [_jsx("span", { className: "text-as-tertiarry text-sm", children: "Total" }), anyspendQuote?.data?.fee && onShowFeeDetail && (_jsx("button", { onClick: onShowFeeDetail, className: "text-as-primary/40 hover:text-as-primary/60 transition-colors", children: _jsx(Info, { className: "h-4 w-4" }) })),
|
|
83
|
-
anyspendQuote?.data?.pointsAmount &&
|
|
84
|
-
anyspendQuote?.data?.pointsAmount > 0 && (_jsx(PointsBadge, { pointsAmount: anyspendQuote.data.pointsAmount, pointsMultiplier: anyspendQuote.data.pointsMultiplier, onClick: () => onShowPointsDetail?.() }))] }), _jsxs("div", { className: "flex flex-col items-end gap-0.5", children: [_jsxs("span", { className: "text-as-primary font-semibold", children: ["$", getTotalAmount(selectedPaymentMethod || FiatPaymentMethod.NONE).toFixed(2)] }), (() => {
|
|
84
|
+
: "bg-as-surface-secondary hover:bg-as-surface-secondary"}`, children: ["$", value] }, value))) }), destinationToken && destinationChainId && !hideDstToken && (_jsx(OrderTokenAmountFiat, { address: _recipientAddress, context: "to", inputValue: destinationAmount || "0", onChangeInput: () => { }, chainId: destinationChainId, setChainId: onDestinationChainChange || (() => { }), token: destinationToken, setToken: onDestinationTokenChange || (() => { }) }))] }), _jsxs("div", { className: "border-as-border-secondary bg-as-surface-secondary mt-4 flex w-full flex-col gap-3 rounded-xl border p-4", children: [_jsxs("div", { className: "flex w-full items-center justify-between gap-2", children: [_jsx("span", { className: "text-as-tertiarry flex items-center text-sm", children: "Recipient" }), _recipientAddress ? (_jsxs("button", { className: "text-as-tertiarry flex h-7 items-center gap-1 text-sm transition-colors hover:text-blue-700", onClick: () => setActivePanel(recipientSelectionPanelIndex), children: [_jsx("span", { className: "text-sm", children: recipientName ? formatUsername(recipientName) : formatAddress(_recipientAddress) }), _jsx(ChevronRight, { size: 16 })] })) : (_jsxs("button", { className: "text-as-tertiarry flex h-7 items-center gap-1 text-sm transition-colors hover:text-blue-700", onClick: () => setActivePanel(5), children: [_jsx(Wallet, { className: "text-as-brand", size: 16 }), "Select recipient", _jsx(ChevronRight, { size: 16 })] }))] }), _jsx("div", { className: "divider w-full" }), _jsxs("div", { className: "flex items-center justify-between gap-4", children: [_jsx("span", { className: "text-as-tertiarry text-sm", children: "Expected to receive" }), _jsxs("div", { className: "flex flex-wrap items-center justify-end gap-1", children: [_jsxs("span", { className: "text-as-primary font-semibold", children: [destinationAmount || "0", " ", dstTokenSymbol || destinationToken?.symbol || ""] }), _jsxs("div", { className: "flex items-center gap-1", children: [_jsxs("span", { className: "text-as-tertiarry text-sm", children: ["on ", destinationChainId ? ALL_CHAINS[destinationChainId]?.name : ""] }), destinationToken && destinationChainId && destinationToken.metadata?.logoURI && (_jsx("img", { src: ALL_CHAINS[destinationChainId]?.logoUrl, alt: "Chain", className: "h-4 w-4 rounded-full" }))] })] })] }), _jsx("div", { className: "divider w-full" }), _jsx("div", { className: "", children: _jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { className: "flex items-center gap-1.5", children: [_jsx("span", { className: "text-as-tertiarry text-sm", children: "Total" }), anyspendQuote?.data?.fee && onShowFeeDetail && (_jsx("button", { onClick: onShowFeeDetail, className: "text-as-primary/40 hover:text-as-primary/60 transition-colors", children: _jsx(Info, { className: "h-4 w-4" }) })), anyspendQuote?.data?.pointsAmount && anyspendQuote?.data?.pointsAmount > 0 && (_jsx(PointsBadge, { pointsAmount: anyspendQuote.data.pointsAmount, pointsMultiplier: anyspendQuote.data.pointsMultiplier, onClick: () => onShowPointsDetail?.() }))] }), _jsxs("div", { className: "flex flex-col items-end gap-0.5", children: [_jsxs("span", { className: "text-as-primary font-semibold", children: ["$", getTotalAmount(selectedPaymentMethod || FiatPaymentMethod.NONE).toFixed(2)] }), (() => {
|
|
85
85
|
// For fiat payments, show the fee from the payment method
|
|
86
86
|
const fiatFee = getFeeFromApi(selectedPaymentMethod || FiatPaymentMethod.NONE);
|
|
87
87
|
if (fiatFee !== null && fiatFee > 0) {
|
|
@@ -12,7 +12,7 @@ export function PanelOnrampPayment(props) {
|
|
|
12
12
|
}
|
|
13
13
|
function PanelOnrampPaymentInner(props) {
|
|
14
14
|
const { srcAmountOnRamp, recipientAddress, isBuyMode, destinationTokenChainId, destinationTokenAddress, selectedDstChainId, selectedDstToken, anyspendQuote, globalAddress, onOrderCreated, onBack, orderType, nft, tournament, payload, recipientEnsName, recipientImageUrl, } = props;
|
|
15
|
-
const { geoData, coinbaseOnrampOptions, coinbaseAvailablePaymentMethods, stripeWeb2Support, isLoading: isLoadingGeoOnramp, } = useGeoOnrampOptions(srcAmountOnRamp);
|
|
15
|
+
const { geoData, coinbaseOnrampOptions, coinbaseAvailablePaymentMethods, stripeOnrampSupport, stripeWeb2Support, isLoading: isLoadingGeoOnramp, } = useGeoOnrampOptions(srcAmountOnRamp);
|
|
16
16
|
const isLoading = isLoadingGeoOnramp;
|
|
17
17
|
const { createOrder, isCreatingOrder } = useAnyspendCreateOnrampOrder({
|
|
18
18
|
onSuccess: data => {
|
|
@@ -91,7 +91,9 @@ function PanelOnrampPaymentInner(props) {
|
|
|
91
91
|
? "Receive NFT at"
|
|
92
92
|
: orderType === "join_tournament"
|
|
93
93
|
? "Join for"
|
|
94
|
-
: "Recipient" }), _jsxs("div", { className: "flex items-center gap-2", children: [recipientImageUrl && (_jsx("img", { src: recipientImageUrl, alt: recipientImageUrl, className: "bg-b3-react-foreground size-7 rounded-full object-cover opacity-100" })), _jsxs("div", { className: "flex flex-col items-end gap-1", children: [recipientEnsName && _jsxs("span", { className: "text-b3-react-foreground/80", children: ["@", recipientEnsName] }), _jsx("span", { className: "text-b3-react-foreground/80", children: centerTruncate(recipientAddress) })] })] })] })), _jsx("div", { className: "border-b3-react-border border-t pt-3", children: _jsxs("div", { className: "flex items-center justify-between", children: [_jsx("p", { className: "text-b3-react-foreground font-semibold", children: "Amount" }), _jsxs("div", { className: "flex flex-col items-end gap-0.5", children: [_jsxs("p", { className: "text-b3-react-foreground hover:text-b3-react-foreground/80 cursor-pointer text-xl font-semibold transition-colors", onClick: onBack, children: ["$", parseFloat(srcAmountOnRamp).toFixed(2)] }), anyspendQuote?.data?.fee?.type === "standard_fee" &&
|
|
94
|
+
: "Recipient" }), _jsxs("div", { className: "flex items-center gap-2", children: [recipientImageUrl && (_jsx("img", { src: recipientImageUrl, alt: recipientImageUrl, className: "bg-b3-react-foreground size-7 rounded-full object-cover opacity-100" })), _jsxs("div", { className: "flex flex-col items-end gap-1", children: [recipientEnsName && _jsxs("span", { className: "text-b3-react-foreground/80", children: ["@", recipientEnsName] }), _jsx("span", { className: "text-b3-react-foreground/80", children: centerTruncate(recipientAddress) })] })] })] })), _jsx("div", { className: "border-b3-react-border border-t pt-3", children: _jsxs("div", { className: "flex items-center justify-between", children: [_jsx("p", { className: "text-b3-react-foreground font-semibold", children: "Amount" }), _jsxs("div", { className: "flex flex-col items-end gap-0.5", children: [_jsxs("p", { className: "text-b3-react-foreground hover:text-b3-react-foreground/80 cursor-pointer text-xl font-semibold transition-colors", onClick: onBack, children: ["$", parseFloat(srcAmountOnRamp).toFixed(2)] }), anyspendQuote?.data?.fee?.type === "standard_fee" &&
|
|
95
|
+
anyspendQuote.data.currencyIn?.amountUsd &&
|
|
96
|
+
anyspendQuote.data.fee.finalFeeBps > 0 && (_jsxs("p", { className: "text-b3-react-foreground/60 text-xs", children: ["incl. $", ((Number(anyspendQuote.data.currencyIn.amountUsd) * anyspendQuote.data.fee.finalFeeBps) /
|
|
95
97
|
10000).toFixed(2), " ", "fee"] }))] })] }) })] })] }), isCreatingOrder ? (_jsxs("div", { className: "bg-b3-react-background border-b3-react-border flex items-center justify-center gap-3 rounded-lg border p-6", children: [_jsx(Loader2, { className: "h-4 w-4 animate-spin" }), _jsx("span", { className: "text-as-primary/70", children: "Creating onramp order..." })] })) : isLoading ? (_jsxs("div", { className: "bg-b3-react-background border-b3-react-border flex items-center justify-center gap-3 rounded-lg border p-6", children: [_jsx(Loader2, { className: "h-4 w-4 animate-spin" }), _jsx("span", { className: "text-as-primary/70", children: "Loading payment options..." })] })) : (_jsxs(_Fragment, { children: [_jsxs("div", { className: "mb-3 flex items-center justify-between", children: [_jsx("h2", { className: "text-lg font-semibold", children: "Payment method" }), _jsx("div", { className: "flex items-center gap-1", children: coinbaseAvailablePaymentMethods.length > 0 &&
|
|
96
98
|
(() => {
|
|
97
99
|
const hasCard = coinbaseAvailablePaymentMethods.some(m => m.id === "CARD");
|
|
@@ -102,5 +104,5 @@ function PanelOnrampPaymentInner(props) {
|
|
|
102
104
|
(() => {
|
|
103
105
|
const method = coinbaseAvailablePaymentMethods[0];
|
|
104
106
|
return (_jsxs("button", { onClick: () => handlePaymentMethodClick("coinbase", method.id), disabled: isCreatingOrder, className: "bg-b3-react-background border-b3-react-border hover:border-as-brand disabled:hover:border-b3-react-border group flex w-full items-center justify-between gap-4 rounded-xl border p-5 transition-all duration-200 hover:shadow-md disabled:cursor-not-allowed disabled:opacity-50", children: [_jsxs("div", { className: "flex items-center gap-4", children: [_jsx("div", { className: "flex h-12 w-12 items-center justify-center rounded-full bg-blue-50", children: _jsx("img", { src: "https://cdn.b3.fun/coinbase-wordmark-blue.svg", alt: "Coinbase", className: "h-6" }) }), _jsxs("div", { className: "flex flex-col items-start text-left", children: [_jsx("h4", { className: "text-b3-react-foreground text-lg font-semibold", children: "Coinbase Pay" }), _jsxs("p", { className: "text-b3-react-foreground/60 text-sm", children: [method.id === "CARD" && "Debit card, bank account, or Coinbase Account", method.id === "FIAT_WALLET" && "Pay with your Coinbase account balance", method.id === "APPLE_PAY" && "Quick payment with Apple Pay", method.id === "ACH_BANK_ACCOUNT" && "Direct bank account transfer"] }), _jsx("div", { className: "mt-1 flex items-center gap-1", children: _jsx("span", { className: "text-xs font-medium text-green-600", children: "Free" }) })] })] }), _jsx(ChevronRight, { className: "text-b3-react-foreground/40 group-hover:text-b3-react-foreground/60 h-5 w-5 transition-colors" })] }));
|
|
105
|
-
})(),
|
|
107
|
+
})(), stripeOnrampSupport && (_jsxs("button", { onClick: () => handlePaymentMethodClick("stripe"), className: "bg-b3-react-background border-b3-react-border hover:border-as-brand group flex w-full items-center justify-between gap-4 rounded-xl border p-5 transition-all duration-200 hover:shadow-md", children: [_jsxs("div", { className: "flex items-center gap-4", children: [_jsx("div", { className: "flex h-12 w-12 items-center justify-center rounded-full bg-purple-50", children: _jsx("img", { src: "https://raw.githubusercontent.com/stripe/stripe.github.io/455f506a628dc3f6c505e3001db45a64e29e9fc3/images/stripe-logo.svg", alt: "Stripe", className: "h-5" }) }), _jsxs("div", { className: "flex flex-col items-start text-left", children: [_jsx("h4", { className: "text-b3-react-foreground text-lg font-semibold", children: "Credit/Debit Card" }), _jsx("p", { className: "text-b3-react-foreground/60 text-sm", children: "Pay via Stripe checkout" }), stripeWeb2Support?.isSupport && stripeWeb2Support.formattedFeeUsd && (_jsx("div", { className: "mt-1", children: _jsxs("span", { className: "text-xs text-gray-500", children: ["$", Number(stripeWeb2Support.formattedFeeUsd).toFixed(2), " fee"] }) }))] })] }), _jsx(ChevronRight, { className: "text-b3-react-foreground/40 group-hover:text-b3-react-foreground/60 h-5 w-5 transition-colors" })] })), stripeWeb2Support.isSupport && (_jsxs("button", { onClick: () => handlePaymentMethodClick("stripe-web2"), className: "bg-b3-react-background border-b3-react-border hover:border-as-brand group flex w-full items-center justify-between gap-4 rounded-xl border p-5 transition-all duration-200 hover:shadow-md", children: [_jsxs("div", { className: "flex items-center gap-4", children: [_jsx("div", { className: "flex h-12 w-12 items-center justify-center rounded-full bg-purple-50", children: _jsx("img", { src: "https://raw.githubusercontent.com/stripe/stripe.github.io/455f506a628dc3f6c505e3001db45a64e29e9fc3/images/stripe-logo.svg", alt: "Stripe", className: "h-5" }) }), _jsxs("div", { className: "flex flex-col items-start text-left", children: [_jsx("h4", { className: "text-b3-react-foreground text-lg font-semibold", children: "Quick Pay" }), _jsx("p", { className: "text-b3-react-foreground/60 text-sm", children: "Credit or debit card" }), stripeWeb2Support.formattedFeeUsd && (_jsx("div", { className: "mt-1", children: _jsxs("span", { className: "text-xs text-gray-500", children: ["$", Number(stripeWeb2Support.formattedFeeUsd).toFixed(2), " fee"] }) }))] })] }), _jsx(ChevronRight, { className: "text-b3-react-foreground/40 group-hover:text-b3-react-foreground/60 h-5 w-5 transition-colors" })] })), _jsxs(Button, { variant: "link", onClick: onBack, className: "text-b3-react-foreground/70 hover:text-b3-react-foreground/90 mt-2 w-full", children: [_jsx(ChevronLeft, { className: "mr-2 h-4 w-4" }), "Back"] })] })] }))] }));
|
|
106
108
|
}
|
|
@@ -30,6 +30,7 @@ export declare function useGeoOnrampOptions(srcFiatAmount: string): {
|
|
|
30
30
|
min?: string;
|
|
31
31
|
max?: string;
|
|
32
32
|
}[];
|
|
33
|
+
stripeOnrampSupport: boolean;
|
|
33
34
|
stripeWeb2Support: {
|
|
34
35
|
isSupport: false;
|
|
35
36
|
} | {
|
|
@@ -38,14 +39,7 @@ export declare function useGeoOnrampOptions(srcFiatAmount: string): {
|
|
|
38
39
|
formattedOnrampUsd: string;
|
|
39
40
|
formattedFeeUsd: string;
|
|
40
41
|
};
|
|
41
|
-
isOnrampSupported:
|
|
42
|
-
isSupport: false;
|
|
43
|
-
} | {
|
|
44
|
-
isSupport: true;
|
|
45
|
-
formattedTotalUsd: string;
|
|
46
|
-
formattedOnrampUsd: string;
|
|
47
|
-
formattedFeeUsd: string;
|
|
48
|
-
};
|
|
42
|
+
isOnrampSupported: boolean;
|
|
49
43
|
isLoading: boolean | undefined;
|
|
50
44
|
isLoadingGeo: boolean;
|
|
51
45
|
isLoadingCoinbaseOnrampOptions: boolean;
|
|
@@ -13,7 +13,7 @@ export function useGeoOnrampOptions(srcFiatAmount) {
|
|
|
13
13
|
// Use existing hooks
|
|
14
14
|
const { geoData, loading: isLoadingGeo, error: geoError } = useGetGeo();
|
|
15
15
|
const { coinbaseOnrampOptions, isLoadingCoinbaseOnrampOptions, coinbaseOnrampOptionsError } = useCoinbaseOnrampOptions(geoData?.country, visitorData);
|
|
16
|
-
const { stripeWeb2Support, isLoadingStripeSupport, stripeSupportError } = useStripeSupport(srcFiatAmount, visitorData);
|
|
16
|
+
const { stripeOnrampSupport, stripeWeb2Support, isLoadingStripeSupport, stripeSupportError } = useStripeSupport(srcFiatAmount, visitorData);
|
|
17
17
|
// Calculate available payment methods based on the amount
|
|
18
18
|
const coinbaseAvailablePaymentMethods = useMemo(() => {
|
|
19
19
|
if (!coinbaseOnrampOptions?.paymentCurrencies?.[0]?.limits || !srcFiatAmount)
|
|
@@ -31,8 +31,9 @@ export function useGeoOnrampOptions(srcFiatAmount) {
|
|
|
31
31
|
geoData,
|
|
32
32
|
coinbaseOnrampOptions,
|
|
33
33
|
coinbaseAvailablePaymentMethods,
|
|
34
|
+
stripeOnrampSupport,
|
|
34
35
|
stripeWeb2Support,
|
|
35
|
-
isOnrampSupported: coinbaseAvailablePaymentMethods.length > 0 || stripeWeb2Support,
|
|
36
|
+
isOnrampSupported: coinbaseAvailablePaymentMethods.length > 0 || stripeOnrampSupport || stripeWeb2Support.isSupport,
|
|
36
37
|
isLoading: isLoadingGeo || isLoadingCoinbaseOnrampOptions || isLoadingStripeSupport || isLoadingVisitorData,
|
|
37
38
|
isLoadingGeo,
|
|
38
39
|
isLoadingCoinbaseOnrampOptions,
|
|
@@ -44,6 +45,7 @@ export function useGeoOnrampOptions(srcFiatAmount) {
|
|
|
44
45
|
geoData,
|
|
45
46
|
coinbaseOnrampOptions,
|
|
46
47
|
coinbaseAvailablePaymentMethods,
|
|
48
|
+
stripeOnrampSupport,
|
|
47
49
|
stripeWeb2Support,
|
|
48
50
|
isLoadingGeo,
|
|
49
51
|
isLoadingCoinbaseOnrampOptions,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { VisitorData } from "../../../anyspend/types/fingerprint";
|
|
2
2
|
export declare function useStripeSupport(usdAmount?: string, visitorData?: VisitorData, isLoadingVisitorData?: boolean): {
|
|
3
|
+
stripeOnrampSupport: boolean;
|
|
3
4
|
stripeWeb2Support: {
|
|
4
5
|
isSupport: false;
|
|
5
6
|
} | {
|
|
@@ -8,6 +8,7 @@ export function useStripeSupport(usdAmount, visitorData, isLoadingVisitorData) {
|
|
|
8
8
|
enabled: !isLoadingVisitorData,
|
|
9
9
|
});
|
|
10
10
|
return useMemo(() => ({
|
|
11
|
+
stripeOnrampSupport: data?.stripeOnramp || false,
|
|
11
12
|
stripeWeb2Support: data?.stripeWeb2 || { isSupport: false },
|
|
12
13
|
isLoadingStripeSupport: isLoading,
|
|
13
14
|
stripeSupportError: error,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { getIpfsUrl } from "../../../../shared/utils/ipfs.js";
|
|
3
4
|
import { client as defaultClient } from "../../../../shared/utils/thirdweb.js";
|
|
4
5
|
import { MediaRenderer } from "thirdweb/react";
|
|
5
6
|
/**
|
|
@@ -25,7 +26,8 @@ export function IPFSMediaRenderer({ src, alt = "Media", className, client = defa
|
|
|
25
26
|
if (!src) {
|
|
26
27
|
return (_jsx("div", { className: className, style: style, "aria-label": alt, children: _jsx("div", { className: "bg-b3-primary-wash flex h-full w-full items-center justify-center rounded-full", children: _jsx("span", { className: "text-b3-grey font-neue-montreal-semibold text-xs", children: alt.charAt(0).toUpperCase() }) }) }));
|
|
27
28
|
}
|
|
28
|
-
// Convert IPFS URLs to HTTP gateway URLs
|
|
29
|
-
// This
|
|
30
|
-
|
|
29
|
+
// Convert IPFS URLs to HTTP gateway URLs using our preferred gateway
|
|
30
|
+
// This avoids Thirdweb's default cloudflare-ipfs.com which can be unreliable
|
|
31
|
+
const resolvedSrc = src.startsWith("ipfs://") ? getIpfsUrl(src) : src;
|
|
32
|
+
return (_jsx(MediaRenderer, { src: resolvedSrc, client: client, alt: alt, className: className, width: width ? width.toString() : undefined, height: height ? height.toString() : undefined, controls: controls, style: style }));
|
|
31
33
|
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Converts an IPFS URL to a gateway URL
|
|
3
3
|
* @param ipfsUrl - URL in format ipfs://CID/path or just the CID
|
|
4
|
-
* @param gatewayIndex - Optional index to specify which gateway to use (0:
|
|
4
|
+
* @param gatewayIndex - Optional index to specify which gateway to use (0: dweb.link, 1: w3s.link, etc.)
|
|
5
5
|
* @returns HTTP URL using the specified IPFS gateway
|
|
6
6
|
* @example
|
|
7
|
-
* // returns 'https://
|
|
7
|
+
* // returns 'https://dweb.link/ipfs/QmUbJ4p.../2.png'
|
|
8
8
|
* getIpfsUrl('ipfs://QmUbJ4pnHMNXGeWWhBFFSEqCGuc6cEtDyz35wQfv7k2TXy/2.png')
|
|
9
|
-
* // returns 'https://
|
|
9
|
+
* // returns 'https://w3s.link/ipfs/QmUbJ4p.../2.png'
|
|
10
10
|
* getIpfsUrl('ipfs://QmUbJ4pnHMNXGeWWhBFFSEqCGuc6cEtDyz35wQfv7k2TXy/2.png', 1)
|
|
11
11
|
*/
|
|
12
12
|
export declare function getIpfsUrl(ipfsUrl: string, gatewayIndex?: number): string;
|
|
@@ -3,22 +3,22 @@
|
|
|
3
3
|
* These gateways must match the allowed list in profileDisplay.ts validateImageUrl()
|
|
4
4
|
*/
|
|
5
5
|
const IPFS_GATEWAYS = [
|
|
6
|
-
"https://
|
|
6
|
+
"https://dweb.link/ipfs", // Primary gateway - Protocol Labs maintained
|
|
7
|
+
"https://w3s.link/ipfs", // web3.storage gateway - reliable
|
|
8
|
+
"https://nftstorage.link/ipfs", // NFT.storage gateway
|
|
9
|
+
"https://gateway.pinata.cloud/ipfs", // Pinata gateway
|
|
7
10
|
"https://ipfs.io/ipfs", // Fallback gateway
|
|
8
|
-
"https://
|
|
9
|
-
"https://dweb.link/ipfs", // Additional option
|
|
10
|
-
"https://nftstorage.link/ipfs", // Additional option
|
|
11
|
-
"https://w3s.link/ipfs", // Additional option
|
|
11
|
+
"https://cloudflare-ipfs.com/ipfs", // Cloudflare gateway (can be slow/unreliable)
|
|
12
12
|
];
|
|
13
13
|
/**
|
|
14
14
|
* Converts an IPFS URL to a gateway URL
|
|
15
15
|
* @param ipfsUrl - URL in format ipfs://CID/path or just the CID
|
|
16
|
-
* @param gatewayIndex - Optional index to specify which gateway to use (0:
|
|
16
|
+
* @param gatewayIndex - Optional index to specify which gateway to use (0: dweb.link, 1: w3s.link, etc.)
|
|
17
17
|
* @returns HTTP URL using the specified IPFS gateway
|
|
18
18
|
* @example
|
|
19
|
-
* // returns 'https://
|
|
19
|
+
* // returns 'https://dweb.link/ipfs/QmUbJ4p.../2.png'
|
|
20
20
|
* getIpfsUrl('ipfs://QmUbJ4pnHMNXGeWWhBFFSEqCGuc6cEtDyz35wQfv7k2TXy/2.png')
|
|
21
|
-
* // returns 'https://
|
|
21
|
+
* // returns 'https://w3s.link/ipfs/QmUbJ4p.../2.png'
|
|
22
22
|
* getIpfsUrl('ipfs://QmUbJ4pnHMNXGeWWhBFFSEqCGuc6cEtDyz35wQfv7k2TXy/2.png', 1)
|
|
23
23
|
*/
|
|
24
24
|
export function getIpfsUrl(ipfsUrl, gatewayIndex = 0) {
|