@b3dotfun/sdk 0.1.63 → 0.1.64-alpha.1

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.
Files changed (35) hide show
  1. package/dist/cjs/anyspend/react/components/AnySpend.d.ts +2 -0
  2. package/dist/cjs/anyspend/react/components/AnySpend.js +25 -8
  3. package/dist/cjs/anyspend/react/components/AnySpendCustomExactIn.js +4 -5
  4. package/dist/cjs/anyspend/react/components/AnySpendDeposit.js +1 -1
  5. package/dist/cjs/anyspend/react/components/common/CryptoPaySection.d.ts +3 -1
  6. package/dist/cjs/anyspend/react/components/common/CryptoPaySection.js +2 -2
  7. package/dist/cjs/anyspend/react/components/common/OrderDetails.js +20 -1
  8. package/dist/cjs/anyspend/react/components/common/OrderTokenAmount.d.ts +3 -1
  9. package/dist/cjs/anyspend/react/components/common/OrderTokenAmount.js +16 -2
  10. package/dist/cjs/anyspend/react/components/common/PaymentStripeWeb2.js +2 -2
  11. package/dist/cjs/anyspend/types/api.d.ts +10 -287
  12. package/dist/esm/anyspend/react/components/AnySpend.d.ts +2 -0
  13. package/dist/esm/anyspend/react/components/AnySpend.js +26 -9
  14. package/dist/esm/anyspend/react/components/AnySpendCustomExactIn.js +4 -5
  15. package/dist/esm/anyspend/react/components/AnySpendDeposit.js +1 -1
  16. package/dist/esm/anyspend/react/components/common/CryptoPaySection.d.ts +3 -1
  17. package/dist/esm/anyspend/react/components/common/CryptoPaySection.js +2 -2
  18. package/dist/esm/anyspend/react/components/common/OrderDetails.js +20 -1
  19. package/dist/esm/anyspend/react/components/common/OrderTokenAmount.d.ts +3 -1
  20. package/dist/esm/anyspend/react/components/common/OrderTokenAmount.js +16 -2
  21. package/dist/esm/anyspend/react/components/common/PaymentStripeWeb2.js +2 -2
  22. package/dist/esm/anyspend/types/api.d.ts +10 -287
  23. package/dist/types/anyspend/react/components/AnySpend.d.ts +2 -0
  24. package/dist/types/anyspend/react/components/common/CryptoPaySection.d.ts +3 -1
  25. package/dist/types/anyspend/react/components/common/OrderTokenAmount.d.ts +3 -1
  26. package/dist/types/anyspend/types/api.d.ts +10 -287
  27. package/package.json +1 -1
  28. package/src/anyspend/react/components/AnySpend.tsx +30 -6
  29. package/src/anyspend/react/components/AnySpendCustomExactIn.tsx +4 -4
  30. package/src/anyspend/react/components/AnySpendDeposit.tsx +1 -0
  31. package/src/anyspend/react/components/common/CryptoPaySection.tsx +4 -0
  32. package/src/anyspend/react/components/common/OrderDetails.tsx +20 -1
  33. package/src/anyspend/react/components/common/OrderTokenAmount.tsx +19 -1
  34. package/src/anyspend/react/components/common/PaymentStripeWeb2.tsx +2 -2
  35. package/src/anyspend/types/api.ts +10 -287
@@ -51,4 +51,6 @@ export declare function AnySpend(props: {
51
51
  classes?: AnySpendClasses;
52
52
  /** When true, allows direct transfer without swap if source and destination token/chain are the same */
53
53
  allowDirectTransfer?: boolean;
54
+ /** Fixed destination token amount (in wei/smallest unit). When provided, user cannot change the amount. */
55
+ destinationTokenAmount?: string;
54
56
  }): import("react/jsx-runtime").JSX.Element;
@@ -12,7 +12,7 @@ import invariant from "invariant";
12
12
  import { ArrowDown, CheckCircle, HistoryIcon, Loader2 } from "lucide-react";
13
13
  import { motion } from "motion/react";
14
14
  import { useCallback, useEffect, useMemo, useRef, useState } from "react";
15
- import { parseUnits } from "viem";
15
+ import { formatUnits, parseUnits } from "viem";
16
16
  import { base, mainnet } from "viem/chains";
17
17
  import { useAutoSelectCryptoPaymentMethod } from "../hooks/useAutoSelectCryptoPaymentMethod.js";
18
18
  import { useConnectedWalletDisplay } from "../hooks/useConnectedWalletDisplay.js";
@@ -54,7 +54,7 @@ export function AnySpend(props) {
54
54
  console.log("[mitch] AnySpend rendered with fingerprintConfig:", props, fingerprintConfig);
55
55
  return (_jsx(AnySpendFingerprintWrapper, { fingerprint: fingerprintConfig, children: _jsx(AnySpendInner, { ...props }) }));
56
56
  }
57
- function AnySpendInner({ sourceChainId, destinationTokenAddress, destinationTokenChainId, mode = "modal", defaultActiveTab = "crypto", loadOrder, hideTransactionHistoryButton, recipientAddress: recipientAddressFromProps, onTokenSelect, onSuccess, customUsdInputValues, hideHeader, hideBottomNavigation = false, disableUrlParamManagement = false, returnToHomeUrl, customRecipientLabel, returnHomeLabel, classes, allowDirectTransfer = false, }) {
57
+ function AnySpendInner({ sourceChainId, destinationTokenAddress, destinationTokenChainId, mode = "modal", defaultActiveTab = "crypto", loadOrder, hideTransactionHistoryButton, recipientAddress: recipientAddressFromProps, onTokenSelect, onSuccess, customUsdInputValues, hideHeader, hideBottomNavigation = false, disableUrlParamManagement = false, returnToHomeUrl, customRecipientLabel, returnHomeLabel, classes, allowDirectTransfer = false, destinationTokenAmount, }) {
58
58
  const searchParams = useSearchParamsSSR();
59
59
  const router = useRouter();
60
60
  const { partnerId } = useB3Config();
@@ -190,6 +190,17 @@ function AnySpendInner({ sourceChainId, destinationTokenAddress, destinationToke
190
190
  useEffect(() => {
191
191
  appliedDstMetadataRef.current = false;
192
192
  }, [selectedDstToken.address, selectedDstToken.chainId]);
193
+ // Prefill destination amount if provided (for fixed amount mode)
194
+ const appliedDestinationAmount = useRef(false);
195
+ useEffect(() => {
196
+ // Only apply when we have real metadata (not default decimals)
197
+ if (destinationTokenAmount && dstTokenMetadata?.decimals && !appliedDestinationAmount.current) {
198
+ appliedDestinationAmount.current = true;
199
+ const formattedAmount = formatUnits(BigInt(destinationTokenAmount), dstTokenMetadata.decimals);
200
+ setDstAmount(formattedAmount);
201
+ setIsSrcInputDirty(false); // Switch to EXACT_OUTPUT mode
202
+ }
203
+ }, [destinationTokenAmount, dstTokenMetadata]);
193
204
  // Load swap configuration from URL on initial render
194
205
  useEffect(() => {
195
206
  // Skip if we've already processed the URL, if we have an order to load, or if URL param management is disabled
@@ -450,9 +461,12 @@ function AnySpendInner({ sourceChainId, destinationTokenAddress, destinationToke
450
461
  anyspendQuote.data.currencyOut?.amount &&
451
462
  anyspendQuote.data.currencyOut?.currency?.decimals) {
452
463
  if (isSrcInputDirty) {
453
- const amount = anyspendQuote.data.currencyOut.amount;
454
- const decimals = anyspendQuote.data.currencyOut.currency.decimals;
455
- setDstAmount(formatTokenAmount(BigInt(amount), decimals, 6, false));
464
+ // Don't override dstAmount if we have a fixed destinationTokenAmount
465
+ if (!destinationTokenAmount) {
466
+ const amount = anyspendQuote.data.currencyOut.amount;
467
+ const decimals = anyspendQuote.data.currencyOut.currency.decimals;
468
+ setDstAmount(formatTokenAmount(BigInt(amount), decimals, 6, false));
469
+ }
456
470
  }
457
471
  else {
458
472
  const amount = anyspendQuote.data.currencyIn.amount;
@@ -462,13 +476,16 @@ function AnySpendInner({ sourceChainId, destinationTokenAddress, destinationToke
462
476
  }
463
477
  else {
464
478
  if (isSrcInputDirty) {
465
- setDstAmount("");
479
+ // Don't reset dstAmount if we have a fixed destinationTokenAmount
480
+ if (!destinationTokenAmount) {
481
+ setDstAmount("");
482
+ }
466
483
  }
467
484
  else {
468
485
  setSrcAmount("");
469
486
  }
470
487
  }
471
- }, [anyspendQuote, isSrcInputDirty]);
488
+ }, [anyspendQuote, isSrcInputDirty, destinationTokenAmount]);
472
489
  useEffect(() => {
473
490
  if (oat?.data?.order.status === "executed" && !onSuccessCalled.current) {
474
491
  console.log("Calling onSuccess");
@@ -867,7 +884,7 @@ function AnySpendInner({ sourceChainId, destinationTokenAddress, destinationToke
867
884
  // Reset payment methods when switching tabs
868
885
  resetPaymentMethods();
869
886
  setSelectedFiatPaymentMethod(FiatPaymentMethod.NONE);
870
- }, setSelectedCryptoPaymentMethod: setSelectedCryptoPaymentMethod, setSelectedFiatPaymentMethod: setSelectedFiatPaymentMethod }), _jsxs("div", { className: "relative flex w-full max-w-[calc(100vw-32px)] flex-col gap-2", children: [activeTab === "crypto" ? (_jsx(CryptoPaySection, { selectedSrcChainId: selectedSrcChainId, setSelectedSrcChainId: setSelectedSrcChainId, selectedSrcToken: selectedSrcToken, setSelectedSrcToken: setSelectedSrcToken, srcAmount: srcAmount, setSrcAmount: setSrcAmount, isSrcInputDirty: isSrcInputDirty, setIsSrcInputDirty: setIsSrcInputDirty, selectedCryptoPaymentMethod: effectiveCryptoPaymentMethod, onSelectCryptoPaymentMethod: () => navigateToPanel(PanelView.CRYPTO_PAYMENT_METHOD, "forward"), anyspendQuote: anyspendQuote, onTokenSelect: onTokenSelect, onShowFeeDetail: () => navigateToPanel(PanelView.FEE_DETAIL, "forward") })) : (_jsx(motion.div, { initial: { opacity: 0, y: 20, filter: "blur(10px)" }, animate: { opacity: 1, y: 0, filter: "blur(0px)" }, transition: { duration: 0.3, delay: 0, ease: "easeInOut" }, children: _jsx(PanelOnramp, { srcAmountOnRamp: srcAmountOnRamp, setSrcAmountOnRamp: setSrcAmountOnRamp, selectedPaymentMethod: selectedFiatPaymentMethod, setActivePanel: (panelIndex) => {
887
+ }, setSelectedCryptoPaymentMethod: setSelectedCryptoPaymentMethod, setSelectedFiatPaymentMethod: setSelectedFiatPaymentMethod }), _jsxs("div", { className: "relative flex w-full max-w-[calc(100vw-32px)] flex-col gap-2", children: [activeTab === "crypto" ? (_jsx(CryptoPaySection, { selectedSrcChainId: selectedSrcChainId, setSelectedSrcChainId: setSelectedSrcChainId, selectedSrcToken: selectedSrcToken, setSelectedSrcToken: setSelectedSrcToken, srcAmount: srcAmount, setSrcAmount: setSrcAmount, isSrcInputDirty: isSrcInputDirty, setIsSrcInputDirty: setIsSrcInputDirty, selectedCryptoPaymentMethod: effectiveCryptoPaymentMethod, onSelectCryptoPaymentMethod: () => navigateToPanel(PanelView.CRYPTO_PAYMENT_METHOD, "forward"), anyspendQuote: anyspendQuote, onTokenSelect: onTokenSelect, onShowFeeDetail: () => navigateToPanel(PanelView.FEE_DETAIL, "forward"), skipAutoMaxOnTokenChange: !!destinationTokenAmount })) : (_jsx(motion.div, { initial: { opacity: 0, y: 20, filter: "blur(10px)" }, animate: { opacity: 1, y: 0, filter: "blur(0px)" }, transition: { duration: 0.3, delay: 0, ease: "easeInOut" }, children: _jsx(PanelOnramp, { srcAmountOnRamp: srcAmountOnRamp, setSrcAmountOnRamp: setSrcAmountOnRamp, selectedPaymentMethod: selectedFiatPaymentMethod, setActivePanel: (panelIndex) => {
871
888
  // Map panel index to navigation with direction
872
889
  const panelsWithForwardNav = [PanelView.FIAT_PAYMENT_METHOD, PanelView.RECIPIENT_SELECTION];
873
890
  if (panelsWithForwardNav.includes(panelIndex)) {
@@ -897,7 +914,7 @@ function AnySpendInner({ sourceChainId, destinationTokenAddress, destinationToke
897
914
  }, children: _jsx("div", { className: "relative flex items-center justify-center transition-opacity", children: _jsx(ArrowDown, { className: "text-as-primary/50 h-5 w-5" }) }) }) })), activeTab === "crypto" && (_jsx(CryptoReceiveSection, { isDepositMode: false, isBuyMode: isBuyMode, effectiveRecipientAddress: effectiveRecipientAddress, recipientName: recipientName || undefined, customRecipientLabel: customRecipientLabel, onSelectRecipient: () => navigateToPanel(PanelView.RECIPIENT_SELECTION, "forward"), dstAmount: dstAmount, dstToken: selectedDstToken, selectedDstChainId: selectedDstChainId, setSelectedDstChainId: setSelectedDstChainId, setSelectedDstToken: setSelectedDstToken, isSrcInputDirty: isSrcInputDirty, onChangeDstAmount: value => {
898
915
  setIsSrcInputDirty(false);
899
916
  setDstAmount(value);
900
- }, anyspendQuote: isDirectTransfer ? undefined : anyspendQuote, onShowPointsDetail: isDirectTransfer ? undefined : () => navigateToPanel(PanelView.POINTS_DETAIL, "forward"), onShowFeeDetail: isDirectTransfer ? undefined : () => navigateToPanel(PanelView.FEE_DETAIL, "forward") }))] }), gasPriceData && !isLoadingGas && activeTab === "crypto" && !isDirectTransfer && (_jsx(GasIndicator, { gasPrice: gasPriceData, className: classes?.gasIndicator || "mt-2 w-full" })), _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.2, ease: "easeInOut" }, className: cn("mt-4 flex w-full max-w-[460px] flex-col gap-2"), children: [_jsx(ShinyButton, { accentColor: "hsl(var(--as-brand))", disabled: btnInfo.disable, onClick: onMainButtonClick, className: (btnInfo.error && classes?.mainButtonError) ||
917
+ }, disableAmountInput: !!destinationTokenAmount, anyspendQuote: isDirectTransfer ? undefined : anyspendQuote, onShowPointsDetail: isDirectTransfer ? undefined : () => navigateToPanel(PanelView.POINTS_DETAIL, "forward"), onShowFeeDetail: isDirectTransfer ? undefined : () => navigateToPanel(PanelView.FEE_DETAIL, "forward") }))] }), gasPriceData && !isLoadingGas && activeTab === "crypto" && !isDirectTransfer && (_jsx(GasIndicator, { gasPrice: gasPriceData, className: classes?.gasIndicator || "mt-2 w-full" })), _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.2, ease: "easeInOut" }, className: cn("mt-4 flex w-full max-w-[460px] flex-col gap-2"), children: [_jsx(ShinyButton, { accentColor: "hsl(var(--as-brand))", disabled: btnInfo.disable, onClick: onMainButtonClick, className: (btnInfo.error && classes?.mainButtonError) ||
901
918
  (btnInfo.disable && classes?.mainButtonDisabled) ||
902
919
  classes?.mainButton ||
903
920
  cn("as-main-button relative w-full", btnInfo.error ? "!bg-as-red" : btnInfo.disable ? "!bg-as-on-surface-2" : "!bg-as-brand"), textClassName: cn(btnInfo.error ? "text-white" : btnInfo.disable ? "text-as-secondary" : "text-white"), children: _jsxs("div", { className: "flex items-center justify-center gap-2", children: [btnInfo.loading && _jsx(Loader2, { className: "h-4 w-4 animate-spin" }), btnInfo.text] }) }), !hideTransactionHistoryButton && (globalAddress || effectiveRecipientAddress) ? (_jsxs(Button, { variant: "link", onClick: onClickHistory, className: classes?.historyButton ||
@@ -77,14 +77,13 @@ function AnySpendCustomExactInInner({ loadOrder, mode = "modal", recipientAddres
77
77
  // Prefill destination amount if provided (for EXACT_OUTPUT mode)
78
78
  const appliedDestinationAmount = useRef(false);
79
79
  useEffect(() => {
80
- if (destinationTokenAmount && !appliedDestinationAmount.current) {
80
+ if (destinationTokenAmount && destinationToken?.decimals && !appliedDestinationAmount.current) {
81
81
  appliedDestinationAmount.current = true;
82
- // Convert wei to human-readable format
83
82
  const formattedAmount = formatUnits(destinationTokenAmount, destinationToken.decimals);
84
83
  setDstAmountInput(formattedAmount);
85
84
  setIsSrcInputDirty(false); // Switch to EXACT_OUTPUT mode
86
85
  }
87
- }, [destinationTokenAmount, destinationToken.decimals, setDstAmountInput, setIsSrcInputDirty]);
86
+ }, [destinationTokenAmount, destinationToken, setDstAmountInput, setIsSrcInputDirty]);
88
87
  const selectedRecipientOrDefault = selectedRecipientAddress ?? recipientAddress;
89
88
  const expectedDstAmountRaw = anyspendQuote?.data?.currencyOut?.amount ?? "0";
90
89
  const buildCustomPayload = (_recipient) => {
@@ -223,8 +222,8 @@ function AnySpendCustomExactInInner({ loadOrder, mode = "modal", recipientAddres
223
222
  };
224
223
  const headerContent = header ? (header({ anyspendPrice: anyspendQuote, isLoadingAnyspendPrice: isLoadingAnyspendQuote })) : (_jsx("div", { className: "mb-4 flex flex-col items-center gap-3 text-center", children: _jsxs("div", { children: [_jsx("h1", { className: "text-as-primary text-xl font-bold", children: actionLabel }), _jsx("p", { className: "text-as-secondary text-sm", children: "Pay from any token to execute a custom exact-in transaction." })] }) }));
225
224
  const mainView = (_jsxs("div", { className: classes?.container ||
226
- "anyspend-custom-exact-in-container mx-auto flex w-[460px] max-w-full flex-col items-center gap-2", children: [headerContent, _jsx("div", { className: "relative flex w-full max-w-[calc(100vw-32px)] flex-col gap-2", children: _jsxs("div", { className: "relative flex w-full max-w-[calc(100vw-32px)] flex-col gap-2", children: [paymentType === "crypto" ? (_jsx(CryptoPaySection, { selectedSrcChainId: selectedSrcChainId, setSelectedSrcChainId: setSelectedSrcChainId, selectedSrcToken: selectedSrcToken, setSelectedSrcToken: setSelectedSrcToken, srcAmount: srcAmount, setSrcAmount: setSrcAmount, isSrcInputDirty: isSrcInputDirty, setIsSrcInputDirty: setIsSrcInputDirty, selectedCryptoPaymentMethod: effectiveCryptoPaymentMethod, onSelectCryptoPaymentMethod: () => setActivePanel(PanelView.CRYPTO_PAYMENT_METHOD), anyspendQuote: anyspendQuote, onTokenSelect: onTokenSelect })) : (_jsx(motion.div, { initial: { opacity: 0, y: 20, filter: "blur(10px)" }, animate: { opacity: 1, y: 0, filter: "blur(0px)" }, transition: { duration: 0.3, delay: 0, ease: "easeInOut" }, children: _jsx(PanelOnramp, { srcAmountOnRamp: srcAmount, setSrcAmountOnRamp: setSrcAmount, selectedPaymentMethod: selectedFiatPaymentMethod, setActivePanel: setActivePanel, _recipientAddress: selectedRecipientOrDefault, destinationToken: selectedDstToken, destinationChainId: selectedDstChainId, dstTokenSymbol: DESTINATION_TOKEN_DETAILS.SYMBOL, hideDstToken: true, destinationAmount: dstAmount, onDestinationTokenChange: () => { }, onDestinationChainChange: () => { }, fiatPaymentMethodIndex: PanelView.FIAT_PAYMENT_METHOD, recipientSelectionPanelIndex: PanelView.RECIPIENT_SELECTION, anyspendQuote: anyspendQuote, onShowPointsDetail: () => setActivePanel(PanelView.POINTS_DETAIL), onShowFeeDetail: () => setActivePanel(PanelView.FEE_DETAIL), customUsdInputValues: customUsdInputValues, customRecipientLabel: customRecipientLabel }) })), _jsx("div", { className: cn("relative -my-1 flex h-0 items-center justify-center", paymentType === "fiat" && "hidden"), children: _jsx(Button, { variant: "ghost", className: classes?.swapDirectionButton ||
227
- "swap-direction-button border-as-stroke bg-as-surface-primary z-10 h-10 w-10 cursor-default rounded-xl border-2 sm:h-8 sm:w-8 sm:rounded-xl", children: _jsx("div", { className: "relative flex items-center justify-center transition-opacity", children: _jsx(ArrowDown, { className: "text-as-primary/50 h-5 w-5" }) }) }) }), paymentType === "crypto" && (_jsx(CryptoReceiveSection, { isDepositMode: false, isBuyMode: false, effectiveRecipientAddress: selectedRecipientOrDefault, recipientName: recipientName || undefined, customRecipientLabel: customRecipientLabel, onSelectRecipient: () => setActivePanel(PanelView.RECIPIENT_SELECTION), dstAmount: isSrcInputDirty ? dstAmount : dstAmountInput, dstToken: selectedDstToken, dstTokenSymbol: DESTINATION_TOKEN_DETAILS.SYMBOL, dstTokenLogoURI: DESTINATION_TOKEN_DETAILS.LOGO_URI, selectedDstChainId: selectedDstChainId, setSelectedDstChainId: () => { }, setSelectedDstToken: () => { }, isSrcInputDirty: isSrcInputDirty, onChangeDstAmount: value => {
225
+ "anyspend-custom-exact-in-container mx-auto flex w-[460px] max-w-full flex-col items-center gap-2", children: [headerContent, _jsx("div", { className: "relative flex w-full max-w-[calc(100vw-32px)] flex-col gap-2", children: _jsxs("div", { className: "relative flex w-full max-w-[calc(100vw-32px)] flex-col gap-2", children: [paymentType === "crypto" ? (_jsx(CryptoPaySection, { selectedSrcChainId: selectedSrcChainId, setSelectedSrcChainId: setSelectedSrcChainId, selectedSrcToken: selectedSrcToken, setSelectedSrcToken: setSelectedSrcToken, srcAmount: srcAmount, setSrcAmount: setSrcAmount, isSrcInputDirty: isSrcInputDirty, setIsSrcInputDirty: setIsSrcInputDirty, selectedCryptoPaymentMethod: effectiveCryptoPaymentMethod, onSelectCryptoPaymentMethod: () => setActivePanel(PanelView.CRYPTO_PAYMENT_METHOD), anyspendQuote: anyspendQuote, onTokenSelect: onTokenSelect, skipAutoMaxOnTokenChange: !!destinationTokenAmount })) : (_jsx(motion.div, { initial: { opacity: 0, y: 20, filter: "blur(10px)" }, animate: { opacity: 1, y: 0, filter: "blur(0px)" }, transition: { duration: 0.3, delay: 0, ease: "easeInOut" }, children: _jsx(PanelOnramp, { srcAmountOnRamp: srcAmount, setSrcAmountOnRamp: setSrcAmount, selectedPaymentMethod: selectedFiatPaymentMethod, setActivePanel: setActivePanel, _recipientAddress: selectedRecipientOrDefault, destinationToken: selectedDstToken, destinationChainId: selectedDstChainId, dstTokenSymbol: DESTINATION_TOKEN_DETAILS.SYMBOL, hideDstToken: true, destinationAmount: dstAmount, onDestinationTokenChange: () => { }, onDestinationChainChange: () => { }, fiatPaymentMethodIndex: PanelView.FIAT_PAYMENT_METHOD, recipientSelectionPanelIndex: PanelView.RECIPIENT_SELECTION, anyspendQuote: anyspendQuote, onShowPointsDetail: () => setActivePanel(PanelView.POINTS_DETAIL), onShowFeeDetail: () => setActivePanel(PanelView.FEE_DETAIL), customUsdInputValues: customUsdInputValues, customRecipientLabel: customRecipientLabel }) })), _jsx("div", { className: cn("relative -my-1 flex h-0 items-center justify-center", paymentType === "fiat" && "hidden"), children: _jsx(Button, { variant: "ghost", className: classes?.swapDirectionButton ||
226
+ "swap-direction-button border-as-stroke bg-as-surface-primary z-10 h-10 w-10 cursor-default rounded-xl border-2 sm:h-8 sm:w-8 sm:rounded-xl", children: _jsx("div", { className: "relative flex items-center justify-center transition-opacity", children: _jsx(ArrowDown, { className: "text-as-primary/50 h-5 w-5" }) }) }) }), paymentType === "crypto" && (_jsx(CryptoReceiveSection, { isDepositMode: false, isBuyMode: false, effectiveRecipientAddress: selectedRecipientOrDefault, recipientName: recipientName || undefined, customRecipientLabel: customRecipientLabel, onSelectRecipient: () => setActivePanel(PanelView.RECIPIENT_SELECTION), dstAmount: isSrcInputDirty && !destinationTokenAmount ? dstAmount : dstAmountInput, dstToken: selectedDstToken, dstTokenSymbol: DESTINATION_TOKEN_DETAILS.SYMBOL, dstTokenLogoURI: DESTINATION_TOKEN_DETAILS.LOGO_URI, selectedDstChainId: selectedDstChainId, setSelectedDstChainId: () => { }, setSelectedDstToken: () => { }, isSrcInputDirty: isSrcInputDirty, onChangeDstAmount: value => {
228
227
  setIsSrcInputDirty(false);
229
228
  setDstAmountInput(value);
230
229
  }, disableAmountInput: !!destinationTokenAmount, anyspendQuote: isDirectTransfer ? undefined : anyspendQuote, onShowPointsDetail: isDirectTransfer ? undefined : () => setActivePanel(PanelView.POINTS_DETAIL), onShowFeeDetail: isDirectTransfer ? undefined : () => setActivePanel(PanelView.FEE_DETAIL) }))] }) }), _jsx(motion.div, { initial: { opacity: 0, y: 20, filter: "blur(10px)" }, animate: { opacity: 1, y: 0, filter: "blur(0px)" }, transition: { duration: 0.3, delay: 0.2, ease: "easeInOut" }, className: cn("mt-4 flex w-full max-w-[460px] flex-col gap-2"), children: _jsx(ShinyButton, { accentColor: "hsl(var(--as-brand))", disabled: btnInfo.disable, onClick: onMainButtonClick, className: (btnInfo.error && classes?.mainButtonError) ||
@@ -206,5 +206,5 @@ export function AnySpendDeposit({ loadOrder, mode = "modal", recipientAddress, p
206
206
  // Deposit view
207
207
  return (_jsxs("div", { className: depositClasses?.form || "anyspend-deposit anyspend-deposit-form relative", children: [shouldShowChainSelection && (_jsxs("button", { onClick: handleBack, className: depositClasses?.backButton ||
208
208
  "anyspend-deposit-back-button text-as-secondary hover:text-as-primary absolute left-4 top-4 z-10 flex items-center gap-1", children: [_jsx("svg", { className: depositClasses?.backIcon || "anyspend-deposit-back-icon h-5 w-5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M15 19l-7-7 7-7" }) }), _jsx("span", { className: depositClasses?.backText || "anyspend-deposit-back-text text-sm", children: "Back" })] })), onClose && (_jsx("button", { onClick: onClose, className: depositClasses?.closeButton ||
209
- "anyspend-deposit-close-button text-as-secondary hover:text-as-primary absolute right-4 top-4 z-10", children: _jsx("svg", { className: "h-6 w-6", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) }) })), _jsx("div", { className: depositClasses?.formContent || cn("anyspend-deposit-form-content", shouldShowChainSelection && "pt-8"), children: isCustomDeposit ? (_jsx(AnySpendCustomExactIn, { loadOrder: loadOrder, mode: mode, recipientAddress: recipientAddress, paymentType: paymentType, sourceTokenAddress: sourceTokenAddress, sourceTokenChainId: selectedChainId, destinationToken: destinationToken, destinationChainId: destinationTokenChainId, orderType: effectiveOrderType, minDestinationAmount: minDestinationAmount, header: header ?? defaultHeader, onSuccess: onSuccess, onOpenCustomModal: onOpenCustomModal, mainFooter: mainFooter, onTokenSelect: onTokenSelect, customUsdInputValues: customUsdInputValues, preferEoa: preferEoa, customExactInConfig: depositContractConfig, returnToHomeUrl: returnToHomeUrl, customRecipientLabel: customRecipientLabel, returnHomeLabel: returnHomeLabel, classes: classes?.customExactIn, allowDirectTransfer: allowDirectTransfer, destinationTokenAmount: destinationTokenAmount }, selectedChainId)) : (_jsx(AnySpend, { loadOrder: loadOrder, mode: mode, defaultActiveTab: paymentType, recipientAddress: recipientAddress, sourceChainId: selectedChainId, destinationTokenAddress: destinationTokenAddress, destinationTokenChainId: destinationTokenChainId, onSuccess: txHash => onSuccess?.(txHash ?? ""), onTokenSelect: onTokenSelect, customUsdInputValues: customUsdInputValues, hideHeader: true, hideBottomNavigation: true, disableUrlParamManagement: true, returnToHomeUrl: returnToHomeUrl, customRecipientLabel: customRecipientLabel, returnHomeLabel: returnHomeLabel, classes: classes?.anySpend, allowDirectTransfer: allowDirectTransfer }, selectedChainId)) }), _jsx(ChainWarningText, { chainId: destinationTokenChainId, classes: classes?.chainWarningText || { root: "px-4 pb-4" } })] }));
209
+ "anyspend-deposit-close-button text-as-secondary hover:text-as-primary absolute right-4 top-4 z-10", children: _jsx("svg", { className: "h-6 w-6", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) }) })), _jsx("div", { className: depositClasses?.formContent || cn("anyspend-deposit-form-content", shouldShowChainSelection && "pt-8"), children: isCustomDeposit ? (_jsx(AnySpendCustomExactIn, { loadOrder: loadOrder, mode: mode, recipientAddress: recipientAddress, paymentType: paymentType, sourceTokenAddress: sourceTokenAddress, sourceTokenChainId: selectedChainId, destinationToken: destinationToken, destinationChainId: destinationTokenChainId, orderType: effectiveOrderType, minDestinationAmount: minDestinationAmount, header: header ?? defaultHeader, onSuccess: onSuccess, onOpenCustomModal: onOpenCustomModal, mainFooter: mainFooter, onTokenSelect: onTokenSelect, customUsdInputValues: customUsdInputValues, preferEoa: preferEoa, customExactInConfig: depositContractConfig, returnToHomeUrl: returnToHomeUrl, customRecipientLabel: customRecipientLabel, returnHomeLabel: returnHomeLabel, classes: classes?.customExactIn, allowDirectTransfer: allowDirectTransfer, destinationTokenAmount: destinationTokenAmount }, selectedChainId)) : (_jsx(AnySpend, { loadOrder: loadOrder, mode: mode, defaultActiveTab: paymentType, recipientAddress: recipientAddress, sourceChainId: selectedChainId, destinationTokenAddress: destinationTokenAddress, destinationTokenChainId: destinationTokenChainId, onSuccess: txHash => onSuccess?.(txHash ?? ""), onTokenSelect: onTokenSelect, customUsdInputValues: customUsdInputValues, hideHeader: true, hideBottomNavigation: true, disableUrlParamManagement: true, returnToHomeUrl: returnToHomeUrl, customRecipientLabel: customRecipientLabel, returnHomeLabel: returnHomeLabel, classes: classes?.anySpend, allowDirectTransfer: allowDirectTransfer, destinationTokenAmount: destinationTokenAmount }, selectedChainId)) }), _jsx(ChainWarningText, { chainId: destinationTokenChainId, classes: classes?.chainWarningText || { root: "px-4 pb-4" } })] }));
210
210
  }
@@ -18,6 +18,8 @@ interface CryptoPaySectionProps {
18
18
  }) => void;
19
19
  onShowFeeDetail?: () => void;
20
20
  classes?: CryptoPaySectionClasses;
21
+ /** When true, skip auto-setting max balance when token changes (used for fixed destination amount mode) */
22
+ skipAutoMaxOnTokenChange?: boolean;
21
23
  }
22
- export declare function CryptoPaySection({ selectedSrcChainId, setSelectedSrcChainId, selectedSrcToken, setSelectedSrcToken, srcAmount, setSrcAmount, isSrcInputDirty, setIsSrcInputDirty, selectedCryptoPaymentMethod, onSelectCryptoPaymentMethod, anyspendQuote, onTokenSelect, onShowFeeDetail, classes, }: CryptoPaySectionProps): import("react/jsx-runtime").JSX.Element;
24
+ export declare function CryptoPaySection({ selectedSrcChainId, setSelectedSrcChainId, selectedSrcToken, setSelectedSrcToken, srcAmount, setSrcAmount, isSrcInputDirty, setIsSrcInputDirty, selectedCryptoPaymentMethod, onSelectCryptoPaymentMethod, anyspendQuote, onTokenSelect, onShowFeeDetail, classes, skipAutoMaxOnTokenChange, }: CryptoPaySectionProps): import("react/jsx-runtime").JSX.Element;
23
25
  export {};
@@ -8,7 +8,7 @@ import { useConnectedWalletDisplay } from "../../hooks/useConnectedWalletDisplay
8
8
  import { CryptoPaymentMethodDisplay } from "./CryptoPaymentMethodDisplay.js";
9
9
  import { OrderTokenAmount } from "./OrderTokenAmount.js";
10
10
  import { TokenBalance } from "./TokenBalance.js";
11
- export function CryptoPaySection({ selectedSrcChainId, setSelectedSrcChainId, selectedSrcToken, setSelectedSrcToken, srcAmount, setSrcAmount, isSrcInputDirty, setIsSrcInputDirty, selectedCryptoPaymentMethod, onSelectCryptoPaymentMethod, anyspendQuote, onTokenSelect, onShowFeeDetail, classes, }) {
11
+ export function CryptoPaySection({ selectedSrcChainId, setSelectedSrcChainId, selectedSrcToken, setSelectedSrcToken, srcAmount, setSrcAmount, isSrcInputDirty, setIsSrcInputDirty, selectedCryptoPaymentMethod, onSelectCryptoPaymentMethod, anyspendQuote, onTokenSelect, onShowFeeDetail, classes, skipAutoMaxOnTokenChange = false, }) {
12
12
  const { data: srcTokenMetadata } = useTokenData(selectedSrcToken?.chainId, selectedSrcToken?.address);
13
13
  // Use custom hook to determine wallet address based on payment method
14
14
  const { walletAddress } = useConnectedWalletDisplay(selectedCryptoPaymentMethod);
@@ -43,7 +43,7 @@ export function CryptoPaySection({ selectedSrcChainId, setSelectedSrcChainId, se
43
43
  "text-as-tertiarry flex h-7 items-center gap-2 text-sm transition-colors focus:!outline-none", onClick: onSelectCryptoPaymentMethod, children: _jsx(CryptoPaymentMethodDisplay, { paymentMethod: selectedCryptoPaymentMethod, connectedAddress: walletAddress, connectedName: connectedName }) })] }), _jsx("div", { className: classes?.inputContainer, children: _jsx(OrderTokenAmount, { address: walletAddress, walletAddress: walletAddress, context: "from", inputValue: srcAmount, onChangeInput: value => {
44
44
  setIsSrcInputDirty(true);
45
45
  setSrcAmount(value);
46
- }, chainId: selectedSrcChainId, setChainId: setSelectedSrcChainId, token: selectedSrcToken, setToken: setSelectedSrcToken, onTokenSelect: onTokenSelect }) }), _jsxs("div", { className: classes?.balanceRow || "flex items-center justify-between", children: [_jsx("div", { className: "text-as-primary/50 flex h-5 items-center text-sm", children: formatDisplayNumber(anyspendQuote?.data?.currencyIn?.amountUsd, {
46
+ }, chainId: selectedSrcChainId, setChainId: setSelectedSrcChainId, token: selectedSrcToken, setToken: setSelectedSrcToken, onTokenSelect: onTokenSelect, skipAutoMaxOnTokenChange: skipAutoMaxOnTokenChange }) }), _jsxs("div", { className: classes?.balanceRow || "flex items-center justify-between", children: [_jsx("div", { className: "text-as-primary/50 flex h-5 items-center text-sm", children: formatDisplayNumber(anyspendQuote?.data?.currencyIn?.amountUsd, {
47
47
  style: "currency",
48
48
  fallback: "",
49
49
  }) }), _jsx(TokenBalance, { token: selectedSrcToken, walletAddress: walletAddress, onChangeInput: value => {
@@ -345,6 +345,25 @@ export const OrderDetails = memo(function OrderDetails({ mode = "modal", order,
345
345
  handlePayment();
346
346
  }
347
347
  }, [isPayableState, isComponentReady, handlePayment]);
348
+ // Auto-redirect to redirectUrl when order is executed (for onramp orders)
349
+ useEffect(() => {
350
+ if (order.status === "executed" && order.onrampMetadata?.redirectUrl) {
351
+ const baseUrl = order.onrampMetadata.redirectUrl;
352
+ try {
353
+ const url = new URL(baseUrl);
354
+ // Prevent Open Redirect vulnerabilities by ensuring the protocol is http or https
355
+ if (url.protocol !== "http:" && url.protocol !== "https:") {
356
+ console.error(`Attempted redirect to a URL with an invalid protocol: ${url.protocol}`);
357
+ return;
358
+ }
359
+ const redirectUrl = `${baseUrl.replace(/\/$/, "")}/${order.id}`;
360
+ window.location.href = redirectUrl;
361
+ }
362
+ catch (error) {
363
+ console.error("Invalid redirect URL provided:", baseUrl, error);
364
+ }
365
+ }
366
+ }, [order.status, order.onrampMetadata?.redirectUrl, order.id]);
348
367
  if (!srcToken || !dstToken) {
349
368
  return _jsx("div", { children: "Loading..." });
350
369
  }
@@ -431,7 +450,7 @@ export const OrderDetails = memo(function OrderDetails({ mode = "modal", order,
431
450
  : `Waiting for deposit ${formattedDepositDeficit} ${srcToken.symbol}`, chainId: order.srcChain, tx: null, isProcessing: true, delay: 0.5 }))] }) })] }) }), depositTxs?.length > 0 && !depositEnoughAmount && order.status === "scanning_deposit_transaction" && (_jsx(InsufficientDepositPayment, { order: order, srcToken: srcToken, depositDeficit: depositDeficit, phantomWalletAddress: phantomWalletAddress, txLoading: txLoading, isSwitchingOrExecuting: isSwitchingOrExecuting, onPayment: handlePayment })), _jsxs("button", { className: classes?.backButton ||
432
451
  "text-b3-primary-blue hover:text-b3-primary-blue/50 order-details-cancel-btn flex w-full items-center justify-center gap-2 underline", onClick: handleBack, children: [_jsx(RefreshCcw, { className: "ml-2 h-4 w-4" }), " Cancel and start over"] })] }));
433
452
  }
434
- return (_jsxs(_Fragment, { children: [_jsx(OrderStatus, { order: order, selectedCryptoPaymentMethod: effectiveCryptoPaymentMethod }), statusDisplay === "processing" && (_jsx(_Fragment, { children: order.onrampMetadata ? (_jsx(PaymentVendorUI, { order: order, dstTokenSymbol: dstToken.symbol })) : effectiveCryptoPaymentMethod === CryptoPaymentMethodType.CONNECT_WALLET ||
453
+ return (_jsxs(_Fragment, { children: [_jsx(OrderStatus, { order: order, selectedCryptoPaymentMethod: effectiveCryptoPaymentMethod }), statusDisplay === "processing" && (_jsx(_Fragment, { children: order.onrampMetadata && order.onrampMetadata.vendor !== "none" ? (_jsx(PaymentVendorUI, { order: order, dstTokenSymbol: dstToken.symbol })) : effectiveCryptoPaymentMethod === CryptoPaymentMethodType.CONNECT_WALLET ||
435
454
  effectiveCryptoPaymentMethod === CryptoPaymentMethodType.GLOBAL_WALLET ? (_jsx(ConnectWalletPayment, { order: order, onPayment: handlePayment, onCancel: handleBack, txLoading: txLoading, isSwitchingOrExecuting: isSwitchingOrExecuting, phantomWalletAddress: phantomWalletAddress, tournament: tournament, nft: nft, cryptoPaymentMethod: effectiveCryptoPaymentMethod, onPaymentMethodChange: onPaymentMethodChange })) : effectiveCryptoPaymentMethod === CryptoPaymentMethodType.TRANSFER_CRYPTO ? (
436
455
  // Transfer Crypto Payment Method - Show new card-based UI
437
456
  _jsx(TransferCryptoDetails, { order: order, recipientName: recipientName, srcToken: srcToken, dstToken: dstToken, tournament: tournament, nft: nft, onBack: handleBack, onPaymentMethodChange: onPaymentMethodChange })) : (_jsxs("div", { className: "order-details-payment-section relative flex w-full flex-1 flex-col", children: [_jsxs("div", { className: "order-details-amount-section flex flex-col gap-1", children: [_jsx("span", { className: "text-as-primary/50 order-details-amount-label", children: "Please send" }), _jsxs("div", { className: "order-details-amount-container flex w-full flex-wrap items-center gap-6 sm:justify-between sm:gap-0", children: [_jsx(CopyToClipboard, { text: roundedUpSrcAmount, onCopy: () => {
@@ -1,5 +1,5 @@
1
1
  import { components } from "../../../../anyspend/types/api";
2
- export declare function OrderTokenAmount({ disabled, inputValue, onChangeInput, context, address, chainId, setChainId, token, setToken, hideTokenSelect, canEditAmount, className, innerClassName, amountClassName, tokenSelectClassName, onTokenSelect, walletAddress, }: {
2
+ export declare function OrderTokenAmount({ disabled, inputValue, onChangeInput, context, address, chainId, setChainId, token, setToken, hideTokenSelect, canEditAmount, className, innerClassName, amountClassName, tokenSelectClassName, onTokenSelect, walletAddress, skipAutoMaxOnTokenChange, }: {
3
3
  disabled?: boolean;
4
4
  inputValue: string;
5
5
  onChangeInput: (value: string) => void;
@@ -19,4 +19,6 @@ export declare function OrderTokenAmount({ disabled, inputValue, onChangeInput,
19
19
  preventDefault: () => void;
20
20
  }) => void;
21
21
  walletAddress?: string | undefined;
22
+ /** When true, skip auto-setting max balance when token changes (used for fixed destination amount mode) */
23
+ skipAutoMaxOnTokenChange?: boolean;
22
24
  }): import("react/jsx-runtime").JSX.Element;
@@ -11,7 +11,7 @@ import { Button, useTokenBalance } from "../../../../global-account/react/index.
11
11
  import { cn } from "../../../../shared/utils/index.js";
12
12
  import { TokenSelector } from "@relayprotocol/relay-kit-ui";
13
13
  import { ChainTokenIcon } from "./ChainTokenIcon.js";
14
- export function OrderTokenAmount({ disabled, inputValue, onChangeInput, context, address, chainId, setChainId, token, setToken, hideTokenSelect = false, canEditAmount = true, className, innerClassName, amountClassName, tokenSelectClassName, onTokenSelect, walletAddress, }) {
14
+ export function OrderTokenAmount({ disabled, inputValue, onChangeInput, context, address, chainId, setChainId, token, setToken, hideTokenSelect = false, canEditAmount = true, className, innerClassName, amountClassName, tokenSelectClassName, onTokenSelect, walletAddress, skipAutoMaxOnTokenChange = false, }) {
15
15
  // Track previous token to detect changes
16
16
  const prevTokenRef = useRef(token.address);
17
17
  // Only get token balance when context is "from" (for setting max amount)
@@ -23,6 +23,11 @@ export function OrderTokenAmount({ disabled, inputValue, onChangeInput, context,
23
23
  // Only handle "from" context
24
24
  if (context !== "from")
25
25
  return;
26
+ // Skip auto-max when in fixed destination amount mode
27
+ if (skipAutoMaxOnTokenChange) {
28
+ prevTokenRef.current = token.address;
29
+ return;
30
+ }
26
31
  // Check if token changed or if this is the initial load with balance
27
32
  const isTokenChanged = prevTokenRef.current !== token.address;
28
33
  if (isTokenChanged && rawBalance) {
@@ -43,7 +48,16 @@ export function OrderTokenAmount({ disabled, inputValue, onChangeInput, context,
43
48
  // Update refs
44
49
  prevTokenRef.current = token.address;
45
50
  }
46
- }, [token.address, token.chainId, token.decimals, chainId, context, onChangeInput, rawBalance]);
51
+ }, [
52
+ token.address,
53
+ token.chainId,
54
+ token.decimals,
55
+ chainId,
56
+ context,
57
+ onChangeInput,
58
+ rawBalance,
59
+ skipAutoMaxOnTokenChange,
60
+ ]);
47
61
  const handleTokenSelect = (newToken) => {
48
62
  const token = {
49
63
  address: newToken.address,
@@ -89,9 +89,9 @@ function StripePaymentForm({ order, clientSecret, onPaymentSuccess, }) {
89
89
  }
90
90
  // At this point TypeScript knows result.paymentIntent exists and error is undefined
91
91
  console.log("@@stripe-web2-payment:success:", JSON.stringify({ orderId: order.id, paymentIntentId: result.paymentIntent.id }, null, 2));
92
- // Payment succeeded without redirect - handle success in the modal
92
+ // Payment succeeded
93
93
  setMessage(null);
94
- // Add waitingForDeposit=true to query params
94
+ // Stay on page and show waiting state (redirect will happen in OrderDetails when order is executed)
95
95
  const currentUrl = new URL(window.location.href);
96
96
  currentUrl.searchParams.set("waitingForDeposit", "true");
97
97
  window.history.replaceState(null, "", currentUrl.toString());