@b3dotfun/sdk 0.0.16 → 0.0.17-alpha.0

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 (102) hide show
  1. package/dist/cjs/anyspend/react/components/AnySpend.d.ts +5 -2
  2. package/dist/cjs/anyspend/react/components/AnySpend.js +264 -55
  3. package/dist/cjs/anyspend/react/components/AnySpendCustom.js +25 -29
  4. package/dist/cjs/anyspend/react/components/common/ConnectWalletPayment.d.ts +15 -0
  5. package/dist/cjs/anyspend/react/components/common/ConnectWalletPayment.js +49 -0
  6. package/dist/cjs/anyspend/react/components/common/CryptoPaymentMethod.d.ts +20 -0
  7. package/dist/cjs/anyspend/react/components/common/CryptoPaymentMethod.js +118 -0
  8. package/dist/cjs/anyspend/react/components/common/FiatPaymentMethod.d.ts +15 -0
  9. package/dist/cjs/anyspend/react/components/common/FiatPaymentMethod.js +71 -0
  10. package/dist/cjs/anyspend/react/components/common/OrderDetails.d.ts +2 -0
  11. package/dist/cjs/anyspend/react/components/common/OrderDetails.js +41 -60
  12. package/dist/cjs/anyspend/react/components/common/OrderDetailsCollapsible.d.ts +18 -0
  13. package/dist/cjs/anyspend/react/components/common/OrderDetailsCollapsible.js +42 -0
  14. package/dist/cjs/anyspend/react/components/common/OrderStatus.js +28 -5
  15. package/dist/cjs/anyspend/react/components/common/OrderTokenAmountFiat.d.ts +13 -0
  16. package/dist/cjs/anyspend/react/components/common/OrderTokenAmountFiat.js +50 -0
  17. package/dist/cjs/anyspend/react/components/common/OrderTokenAmountNew.d.ts +16 -0
  18. package/dist/cjs/anyspend/react/components/common/OrderTokenAmountNew.js +63 -0
  19. package/dist/cjs/anyspend/react/components/common/PanelOnramp.d.ts +11 -1
  20. package/dist/cjs/anyspend/react/components/common/PanelOnramp.js +53 -15
  21. package/dist/cjs/anyspend/react/components/common/PanelOnrampPayment.js +1 -12
  22. package/dist/cjs/anyspend/react/components/common/PaymentStripeWeb2.js +4 -12
  23. package/dist/cjs/anyspend/react/components/common/StepProgress.d.ts +11 -0
  24. package/dist/cjs/anyspend/react/components/common/StepProgress.js +22 -0
  25. package/dist/cjs/anyspend/react/components/common/TransferCryptoDetails.d.ts +16 -0
  26. package/dist/cjs/anyspend/react/components/common/TransferCryptoDetails.js +73 -0
  27. package/dist/cjs/anyspend/react/components/index.d.ts +3 -0
  28. package/dist/cjs/anyspend/react/components/index.js +7 -1
  29. package/dist/cjs/anyspend/react/components/webview/WebviewOnrampPayment.js +1 -10
  30. package/dist/cjs/anyspend/utils/format.d.ts +1 -0
  31. package/dist/cjs/anyspend/utils/format.js +23 -10
  32. package/dist/cjs/anyspend/utils/number.d.ts +7 -0
  33. package/dist/cjs/anyspend/utils/number.js +18 -0
  34. package/dist/esm/anyspend/react/components/AnySpend.d.ts +5 -2
  35. package/dist/esm/anyspend/react/components/AnySpend.js +266 -57
  36. package/dist/esm/anyspend/react/components/AnySpendCustom.js +27 -31
  37. package/dist/esm/anyspend/react/components/common/ConnectWalletPayment.d.ts +15 -0
  38. package/dist/esm/anyspend/react/components/common/ConnectWalletPayment.js +46 -0
  39. package/dist/esm/anyspend/react/components/common/CryptoPaymentMethod.d.ts +20 -0
  40. package/dist/esm/anyspend/react/components/common/CryptoPaymentMethod.js +114 -0
  41. package/dist/esm/anyspend/react/components/common/FiatPaymentMethod.d.ts +15 -0
  42. package/dist/esm/anyspend/react/components/common/FiatPaymentMethod.js +67 -0
  43. package/dist/esm/anyspend/react/components/common/OrderDetails.d.ts +2 -0
  44. package/dist/esm/anyspend/react/components/common/OrderDetails.js +43 -62
  45. package/dist/esm/anyspend/react/components/common/OrderDetailsCollapsible.d.ts +18 -0
  46. package/dist/esm/anyspend/react/components/common/OrderDetailsCollapsible.js +36 -0
  47. package/dist/esm/anyspend/react/components/common/OrderStatus.js +27 -4
  48. package/dist/esm/anyspend/react/components/common/OrderTokenAmountFiat.d.ts +13 -0
  49. package/dist/esm/anyspend/react/components/common/OrderTokenAmountFiat.js +47 -0
  50. package/dist/esm/anyspend/react/components/common/OrderTokenAmountNew.d.ts +16 -0
  51. package/dist/esm/anyspend/react/components/common/OrderTokenAmountNew.js +60 -0
  52. package/dist/esm/anyspend/react/components/common/PanelOnramp.d.ts +11 -1
  53. package/dist/esm/anyspend/react/components/common/PanelOnramp.js +54 -16
  54. package/dist/esm/anyspend/react/components/common/PanelOnrampPayment.js +1 -12
  55. package/dist/esm/anyspend/react/components/common/PaymentStripeWeb2.js +7 -15
  56. package/dist/esm/anyspend/react/components/common/StepProgress.d.ts +11 -0
  57. package/dist/esm/anyspend/react/components/common/StepProgress.js +19 -0
  58. package/dist/esm/anyspend/react/components/common/TransferCryptoDetails.d.ts +16 -0
  59. package/dist/esm/anyspend/react/components/common/TransferCryptoDetails.js +70 -0
  60. package/dist/esm/anyspend/react/components/index.d.ts +3 -0
  61. package/dist/esm/anyspend/react/components/index.js +3 -0
  62. package/dist/esm/anyspend/react/components/webview/WebviewOnrampPayment.js +1 -10
  63. package/dist/esm/anyspend/utils/format.d.ts +1 -0
  64. package/dist/esm/anyspend/utils/format.js +23 -10
  65. package/dist/esm/anyspend/utils/number.d.ts +7 -0
  66. package/dist/esm/anyspend/utils/number.js +17 -0
  67. package/dist/styles/index.css +1 -1
  68. package/dist/types/anyspend/react/components/AnySpend.d.ts +5 -2
  69. package/dist/types/anyspend/react/components/common/ConnectWalletPayment.d.ts +15 -0
  70. package/dist/types/anyspend/react/components/common/CryptoPaymentMethod.d.ts +20 -0
  71. package/dist/types/anyspend/react/components/common/FiatPaymentMethod.d.ts +15 -0
  72. package/dist/types/anyspend/react/components/common/OrderDetails.d.ts +2 -0
  73. package/dist/types/anyspend/react/components/common/OrderDetailsCollapsible.d.ts +18 -0
  74. package/dist/types/anyspend/react/components/common/OrderTokenAmountFiat.d.ts +13 -0
  75. package/dist/types/anyspend/react/components/common/OrderTokenAmountNew.d.ts +16 -0
  76. package/dist/types/anyspend/react/components/common/PanelOnramp.d.ts +11 -1
  77. package/dist/types/anyspend/react/components/common/StepProgress.d.ts +11 -0
  78. package/dist/types/anyspend/react/components/common/TransferCryptoDetails.d.ts +16 -0
  79. package/dist/types/anyspend/react/components/index.d.ts +3 -0
  80. package/dist/types/anyspend/utils/format.d.ts +1 -0
  81. package/dist/types/anyspend/utils/number.d.ts +7 -0
  82. package/package.json +1 -1
  83. package/src/anyspend/react/components/AnySpend.tsx +535 -177
  84. package/src/anyspend/react/components/AnySpendCustom.tsx +33 -38
  85. package/src/anyspend/react/components/common/ConnectWalletPayment.tsx +124 -0
  86. package/src/anyspend/react/components/common/CryptoPaymentMethod.tsx +227 -0
  87. package/src/anyspend/react/components/common/FiatPaymentMethod.tsx +173 -0
  88. package/src/anyspend/react/components/common/OrderDetails.tsx +123 -250
  89. package/src/anyspend/react/components/common/OrderDetailsCollapsible.tsx +156 -0
  90. package/src/anyspend/react/components/common/OrderStatus.tsx +47 -24
  91. package/src/anyspend/react/components/common/OrderTokenAmountFiat.tsx +147 -0
  92. package/src/anyspend/react/components/common/OrderTokenAmountNew.tsx +215 -0
  93. package/src/anyspend/react/components/common/PanelOnramp.tsx +215 -62
  94. package/src/anyspend/react/components/common/PanelOnrampPayment.tsx +1 -14
  95. package/src/anyspend/react/components/common/PaymentStripeWeb2.tsx +42 -99
  96. package/src/anyspend/react/components/common/StepProgress.tsx +104 -0
  97. package/src/anyspend/react/components/common/TransferCryptoDetails.tsx +261 -0
  98. package/src/anyspend/react/components/index.ts +3 -0
  99. package/src/anyspend/react/components/webview/WebviewOnrampPayment.tsx +1 -11
  100. package/src/anyspend/utils/format.ts +24 -11
  101. package/src/anyspend/utils/number.ts +18 -0
  102. package/src/styles/index.css +30 -11
@@ -2,9 +2,11 @@
2
2
 
3
3
  import { getDefaultToken, USDC_BASE } from "@b3dotfun/sdk/anyspend";
4
4
  import {
5
+ useAnyspendCreateOnrampOrder,
5
6
  useAnyspendCreateOrder,
6
7
  useAnyspendOrderAndTransactions,
7
8
  useAnyspendQuote,
9
+ useGeoOnrampOptions,
8
10
  } from "@b3dotfun/sdk/anyspend/react";
9
11
  import {
10
12
  Button,
@@ -22,13 +24,17 @@ import { cn } from "@b3dotfun/sdk/shared/utils/cn";
22
24
  import { shortenAddress } from "@b3dotfun/sdk/shared/utils/formatAddress";
23
25
  import { formatDisplayNumber, formatTokenAmount } from "@b3dotfun/sdk/shared/utils/number";
24
26
  import invariant from "invariant";
25
- import { ArrowDown, ChevronRightCircle, ChevronsUpDown, CircleAlert, ClipboardIcon, HistoryIcon } from "lucide-react";
27
+ import { ArrowDown, ChevronLeft, ChevronRightCircle, ChevronsUpDown, CircleAlert, HistoryIcon } from "lucide-react";
26
28
  import { motion } from "motion/react";
27
29
  import { useCallback, useEffect, useMemo, useRef, useState } from "react";
28
30
  import { toast } from "sonner";
29
31
  import { parseUnits } from "viem";
30
32
  import { b3Sepolia, base, mainnet, sepolia } from "viem/chains";
33
+ import { useAccount } from "wagmi";
31
34
  import { components } from "../../types/api";
35
+ import { AnySpendFingerprintWrapper, getFingerprintConfig } from "./AnySpendFingerprintWrapper";
36
+ import { CryptoPaymentMethod, PaymentMethod } from "./common/CryptoPaymentMethod";
37
+ import { FiatPaymentMethod, FiatPaymentMethodComponent } from "./common/FiatPaymentMethod";
32
38
  import { OrderDetails, OrderDetailsLoadingView } from "./common/OrderDetails";
33
39
  import { OrderHistory } from "./common/OrderHistory";
34
40
  import { OrderStatus } from "./common/OrderStatus";
@@ -36,7 +42,6 @@ import { OrderTokenAmount } from "./common/OrderTokenAmount";
36
42
  import { PanelOnramp } from "./common/PanelOnramp";
37
43
  import { PanelOnrampPayment } from "./common/PanelOnrampPayment";
38
44
  import { TokenBalance } from "./common/TokenBalance";
39
- import { EnterRecipientModal } from "./modals/EnterRecipientModal";
40
45
 
41
46
  export interface RecipientOption {
42
47
  address: string;
@@ -51,11 +56,33 @@ export enum PanelView {
51
56
  ORDER_DETAILS,
52
57
  LOADING,
53
58
  FIAT_PAYMENT,
59
+ RECIPIENT_SELECTION,
60
+ CRYPTO_PAYMENT_METHOD,
61
+ FIAT_PAYMENT_METHOD,
54
62
  }
55
63
 
56
64
  const ANYSPEND_RECIPIENTS_KEY = "anyspend_recipients";
57
65
 
58
- export function AnySpend({
66
+ export function AnySpend(props: {
67
+ destinationTokenAddress?: string;
68
+ destinationTokenChainId?: number;
69
+ isMainnet?: boolean;
70
+ mode?: "page" | "modal";
71
+ defaultActiveTab?: "crypto" | "fiat";
72
+ loadOrder?: string;
73
+ hideTransactionHistoryButton?: boolean;
74
+ recipientAddress?: string;
75
+ }) {
76
+ const fingerprintConfig = getFingerprintConfig();
77
+
78
+ return (
79
+ <AnySpendFingerprintWrapper fingerprint={fingerprintConfig}>
80
+ <AnySpendInner {...props} />
81
+ </AnySpendFingerprintWrapper>
82
+ );
83
+ }
84
+
85
+ function AnySpendInner({
59
86
  destinationTokenAddress,
60
87
  destinationTokenChainId,
61
88
  isMainnet = true,
@@ -77,6 +104,9 @@ export function AnySpend({
77
104
  const searchParams = useSearchParamsSSR();
78
105
  const router = useRouter();
79
106
 
107
+ // Get wagmi account state for wallet connection
108
+ const wagmiAccount = useAccount();
109
+
80
110
  // Determine if we're in "buy mode" based on whether destination token props are provided
81
111
  const isBuyMode = !!(destinationTokenAddress && destinationTokenChainId);
82
112
 
@@ -109,6 +139,10 @@ export function AnySpend({
109
139
 
110
140
  const [activePanel, setActivePanel] = useState<PanelView>(loadOrder ? PanelView.ORDER_DETAILS : PanelView.MAIN);
111
141
  const [customRecipients, setCustomRecipients] = useState<RecipientOption[]>([]);
142
+ // Add state for selected payment method
143
+ const [selectedPaymentMethod, setSelectedPaymentMethod] = useState<PaymentMethod>(PaymentMethod.NONE);
144
+ // Add state for selected fiat payment method
145
+ const [selectedFiatPaymentMethod, setSelectedFiatPaymentMethod] = useState<FiatPaymentMethod>(FiatPaymentMethod.NONE);
112
146
  // const [newRecipientAddress, setNewRecipientAddress] = useState("");
113
147
  // const recipientInputRef = useRef<HTMLInputElement>(null);
114
148
 
@@ -588,9 +622,38 @@ export function AnySpend({
588
622
  // setNewRecipientAddress("");
589
623
  setActivePanel(PanelView.ORDER_DETAILS);
590
624
 
591
- // Add orderId to URL for persistence
592
- const params = new URLSearchParams();
625
+ // Debug: Check payment method before setting URL
626
+ console.log("Creating order - selectedPaymentMethod:", selectedPaymentMethod);
627
+
628
+ // Add orderId and payment method to URL for persistence
629
+ const params = new URLSearchParams(searchParams.toString()); // Preserve existing params
630
+ params.set("orderId", orderId);
631
+ if (selectedPaymentMethod !== PaymentMethod.NONE) {
632
+ console.log("Setting paymentMethod in URL:", selectedPaymentMethod);
633
+ params.set("paymentMethod", selectedPaymentMethod);
634
+ } else {
635
+ console.log("Payment method is NONE, not setting in URL");
636
+ }
637
+ console.log("Final URL params:", params.toString());
638
+ router.push(`${window.location.pathname}?${params.toString()}`);
639
+ },
640
+ onError: error => {
641
+ console.error(error);
642
+ toast.error("Failed to create order: " + error.message);
643
+ },
644
+ });
645
+
646
+ // Add onramp order creation hook
647
+ const { createOrder: createOnrampOrder, isCreatingOrder: isCreatingOnrampOrder } = useAnyspendCreateOnrampOrder({
648
+ onSuccess: data => {
649
+ const orderId = data.data.id;
650
+ setOrderId(orderId);
651
+ setActivePanel(PanelView.ORDER_DETAILS);
652
+
653
+ // Add orderId and payment method to URL for persistence
654
+ const params = new URLSearchParams(searchParams.toString());
593
655
  params.set("orderId", orderId);
656
+ params.set("paymentMethod", "fiat");
594
657
  router.push(`${window.location.pathname}?${params.toString()}`);
595
658
  },
596
659
  onError: error => {
@@ -599,24 +662,54 @@ export function AnySpend({
599
662
  },
600
663
  });
601
664
 
665
+ // Get geo-based onramp options for fiat payments
666
+ const { geoData, coinbaseAvailablePaymentMethods, isStripeOnrampSupported, stripeWeb2Support } = useGeoOnrampOptions(
667
+ isMainnet,
668
+ srcAmountOnRamp,
669
+ );
670
+
602
671
  // Determine button state and text
603
672
  const btnInfo: { text: string; disable: boolean; error: boolean } = useMemo(() => {
604
673
  if (activeInputAmountInWei === "0") return { text: "Enter an amount", disable: true, error: false };
605
674
  if (isLoadingAnyspendQuote) return { text: "Loading...", disable: true, error: false };
606
675
  if (!recipientAddress) return { text: "Select recipient", disable: false, error: false };
607
- if (isCreatingOrder) return { text: "Creating order...", disable: true, error: false };
676
+ if (isCreatingOrder || isCreatingOnrampOrder) return { text: "Creating order...", disable: true, error: false };
608
677
  if (!anyspendQuote || !anyspendQuote.success) return { text: "Get rate error", disable: true, error: true };
609
- if (activeTab === "fiat") return { text: "Continue to payment", disable: false, error: false };
610
- return { text: isBuyMode ? `Buy ${selectedDstToken.symbol}` : "Swap", disable: false, error: false };
678
+
679
+ if (activeTab === "crypto") {
680
+ // If no payment method selected, show "Choose payment method"
681
+ if (selectedPaymentMethod === PaymentMethod.NONE) {
682
+ return { text: "Choose payment method", disable: false, error: false };
683
+ }
684
+ // If payment method selected, show appropriate action
685
+ if (selectedPaymentMethod === PaymentMethod.CONNECT_WALLET) {
686
+ return { text: "Swap", disable: false, error: false };
687
+ }
688
+ if (selectedPaymentMethod === PaymentMethod.TRANSFER_CRYPTO) {
689
+ return { text: "Continue to payment", disable: false, error: false };
690
+ }
691
+ }
692
+
693
+ if (activeTab === "fiat") {
694
+ // If no fiat payment method selected, show "Select payment method"
695
+ if (selectedFiatPaymentMethod === FiatPaymentMethod.NONE) {
696
+ return { text: "Select payment method", disable: false, error: false };
697
+ }
698
+ // If payment method is selected, show "Buy"
699
+ return { text: "Buy", disable: false, error: false };
700
+ }
701
+
702
+ return { text: "Buy", disable: false, error: false };
611
703
  }, [
612
704
  activeInputAmountInWei,
613
705
  isLoadingAnyspendQuote,
614
706
  recipientAddress,
615
707
  isCreatingOrder,
708
+ isCreatingOnrampOrder,
616
709
  anyspendQuote,
617
710
  activeTab,
618
- isBuyMode,
619
- selectedDstToken.symbol,
711
+ selectedPaymentMethod,
712
+ selectedFiatPaymentMethod,
620
713
  ]);
621
714
 
622
715
  // Handle main button click
@@ -624,7 +717,7 @@ export function AnySpend({
624
717
  if (btnInfo.disable) return;
625
718
 
626
719
  if (!recipientAddress) {
627
- setIsOpenPasteRecipientAddressModal(true);
720
+ setActivePanel(PanelView.RECIPIENT_SELECTION);
628
721
  return;
629
722
  }
630
723
 
@@ -633,10 +726,60 @@ export function AnySpend({
633
726
  invariant(recipientAddress, "Recipient address is not found");
634
727
 
635
728
  if (activeTab === "fiat") {
636
- setActivePanel(PanelView.FIAT_PAYMENT);
729
+ // If no fiat payment method selected, show payment method selection
730
+ if (selectedFiatPaymentMethod === FiatPaymentMethod.NONE) {
731
+ setActivePanel(PanelView.FIAT_PAYMENT_METHOD);
732
+ return;
733
+ }
734
+ // If payment method is selected, create order directly
735
+ await handleFiatOrder(selectedFiatPaymentMethod);
637
736
  return;
638
737
  }
639
738
 
739
+ if (activeTab === "crypto") {
740
+ // If no payment method selected, show payment method selection
741
+ if (selectedPaymentMethod === PaymentMethod.NONE) {
742
+ console.log("No payment method selected, showing selection panel");
743
+ setActivePanel(PanelView.CRYPTO_PAYMENT_METHOD);
744
+ return;
745
+ }
746
+
747
+ // If payment method is selected, create order with payment method info
748
+ if (
749
+ selectedPaymentMethod === PaymentMethod.CONNECT_WALLET ||
750
+ selectedPaymentMethod === PaymentMethod.TRANSFER_CRYPTO
751
+ ) {
752
+ console.log("Creating crypto order with payment method:", selectedPaymentMethod);
753
+ await handleCryptoSwap(selectedPaymentMethod);
754
+ return;
755
+ }
756
+ }
757
+ } catch (err: any) {
758
+ console.error(err);
759
+ toast.error("Failed to create order: " + err.message);
760
+ }
761
+ };
762
+
763
+ const onClickHistory = () => {
764
+ setOrderId(undefined);
765
+ setActivePanel(PanelView.HISTORY);
766
+ // Remove orderId and paymentMethod from URL when going back to history
767
+ const params = new URLSearchParams(searchParams.toString());
768
+ params.delete("orderId");
769
+ params.delete("paymentMethod");
770
+ router.push(`${window.location.pathname}?${params.toString()}`);
771
+ };
772
+
773
+ // Handle crypto swap creation
774
+ const handleCryptoSwap = async (method: PaymentMethod) => {
775
+ try {
776
+ invariant(anyspendQuote, "Relay price is not found");
777
+ invariant(recipientAddress, "Recipient address is not found");
778
+
779
+ // Debug: Check payment method values
780
+ console.log("handleCryptoSwap - method parameter:", method);
781
+ console.log("handleCryptoSwap - selectedPaymentMethod state:", selectedPaymentMethod);
782
+
640
783
  const srcAmountBigInt = parseUnits(srcAmount.replace(/,/g, ""), selectedSrcToken.decimals);
641
784
 
642
785
  createOrder({
@@ -659,13 +802,74 @@ export function AnySpend({
659
802
  }
660
803
  };
661
804
 
662
- const onClickHistory = () => {
663
- setOrderId(undefined);
664
- setActivePanel(PanelView.HISTORY);
665
- // Remove orderId from URL when going back to history
666
- const params = new URLSearchParams(searchParams.toString());
667
- params.delete("orderId");
668
- router.push(`${window.location.pathname}?${params.toString()}`);
805
+ // Handle fiat onramp order creation
806
+ const handleFiatOrder = async (paymentMethod: FiatPaymentMethod) => {
807
+ try {
808
+ invariant(anyspendQuote, "Relay price is not found");
809
+ invariant(recipientAddress, "Recipient address is not found");
810
+
811
+ if (!srcAmountOnRamp || parseFloat(srcAmountOnRamp) <= 0) {
812
+ toast.error("Please enter a valid amount");
813
+ return;
814
+ }
815
+
816
+ // Determine vendor and payment method string based on selected payment method
817
+ let vendor: components["schemas"]["OnrampMetadata"]["vendor"];
818
+ let paymentMethodString = "";
819
+
820
+ if (paymentMethod === FiatPaymentMethod.COINBASE_PAY) {
821
+ if (coinbaseAvailablePaymentMethods.length === 0) {
822
+ toast.error("Coinbase Pay not available");
823
+ return;
824
+ }
825
+ vendor = "coinbase";
826
+ paymentMethodString = coinbaseAvailablePaymentMethods[0]?.id || ""; // Use first available payment method ID
827
+ } else if (paymentMethod === FiatPaymentMethod.STRIPE) {
828
+ if (!isStripeOnrampSupported && (!stripeWeb2Support || !stripeWeb2Support.isSupport)) {
829
+ toast.error("Stripe not available");
830
+ return;
831
+ }
832
+ vendor = stripeWeb2Support && stripeWeb2Support.isSupport ? "stripe-web2" : "stripe";
833
+ paymentMethodString = "";
834
+ } else {
835
+ toast.error("Please select a payment method");
836
+ return;
837
+ }
838
+
839
+ const getDstToken = (): components["schemas"]["Token"] => {
840
+ if (isBuyMode) {
841
+ invariant(destinationTokenAddress, "destinationTokenAddress is required");
842
+ return {
843
+ ...selectedDstToken,
844
+ chainId: destinationTokenChainId || selectedDstChainId,
845
+ address: destinationTokenAddress,
846
+ };
847
+ }
848
+ return selectedDstToken;
849
+ };
850
+
851
+ createOnrampOrder({
852
+ isMainnet,
853
+ recipientAddress,
854
+ orderType: "swap",
855
+ dstChain: getDstToken().chainId,
856
+ dstToken: getDstToken(),
857
+ srcFiatAmount: srcAmountOnRamp,
858
+ onramp: {
859
+ vendor: vendor,
860
+ paymentMethod: paymentMethodString,
861
+ country: geoData?.country || "US",
862
+ ipAddress: geoData?.ip,
863
+ redirectUrl:
864
+ window.location.origin === "https://basement.fun" ? "https://basement.fun/deposit" : window.location.origin,
865
+ },
866
+ expectedDstAmount: anyspendQuote?.data?.currencyOut?.amount?.toString() || "0",
867
+ creatorAddress: globalAddress,
868
+ });
869
+ } catch (err: any) {
870
+ console.error(err);
871
+ toast.error("Failed to create order: " + err.message);
872
+ }
669
873
  };
670
874
 
671
875
  // Open a dynamic modal
@@ -686,9 +890,10 @@ export function AnySpend({
686
890
  const onSelectOrder = (selectedOrderId: string) => {
687
891
  setActivePanel(PanelView.MAIN);
688
892
  setOrderId(selectedOrderId);
689
- // Update URL with the new orderId
893
+ // Update URL with the new orderId and preserve existing parameters
690
894
  const params = new URLSearchParams(searchParams.toString());
691
895
  params.set("orderId", selectedOrderId);
896
+ // Keep existing paymentMethod if present
692
897
  router.push(`${window.location.pathname}?${params.toString()}`);
693
898
  };
694
899
 
@@ -706,8 +911,6 @@ export function AnySpend({
706
911
  window.scrollTo({ top: 0, behavior: "smooth" });
707
912
  }, [activePanel]);
708
913
 
709
- const [isOpenPasteRecipientAddressModal, setIsOpenPasteRecipientAddressModal] = useState(false);
710
-
711
914
  const calculatePriceImpact = (inputUsd?: string | number, outputUsd?: string | number) => {
712
915
  if (!inputUsd || !outputUsd) {
713
916
  return { percentage: "0.00", isNegative: false };
@@ -760,11 +963,12 @@ export function AnySpend({
760
963
  onBack={() => {
761
964
  setOrderId(undefined);
762
965
  setActivePanel(PanelView.MAIN);
966
+ setSelectedPaymentMethod(PaymentMethod.NONE); // Reset payment method when going back
763
967
  }}
764
968
  />
765
969
  </>
766
970
  )}
767
- {mode === "page" && <div className="h-12" />}
971
+ {/* {mode === "page" && <div className="h-12" />} */}
768
972
  </div>
769
973
  </div>
770
974
  );
@@ -790,33 +994,43 @@ export function AnySpend({
790
994
  )}
791
995
 
792
996
  {/* Tab section */}
793
- <div className="bg-as-on-surface-1 relative mx-auto mb-4 grid h-10 w-fit min-w-[180px] grid-cols-2 rounded-xl">
794
- <div
795
- className={cn(
796
- "bg-as-brand absolute bottom-0 left-0 top-0 z-0 rounded-xl transition-transform duration-100",
797
- "h-full w-1/2",
798
- activeTab === "fiat" ? "translate-x-full" : "translate-x-0",
799
- )}
800
- style={{ willChange: "transform" }}
801
- />
802
- <button
803
- className={cn(
804
- "relative z-10 h-full w-full rounded-xl px-6 text-sm font-medium transition-colors duration-100",
805
- activeTab === "crypto" ? "text-white" : "text-as-primary/70 hover:bg-as-on-surface-2 bg-transparent",
806
- )}
807
- onClick={() => setActiveTab("crypto")}
808
- >
809
- Crypto
810
- </button>
811
- <button
812
- className={cn(
813
- "relative z-10 h-full w-full rounded-xl px-6 text-sm font-medium transition-colors duration-100",
814
- activeTab === "fiat" ? "text-white" : "text-as-primary/70 hover:bg-as-on-surface-2 bg-transparent",
815
- )}
816
- onClick={() => setActiveTab("fiat")}
817
- >
818
- Fiat
819
- </button>
997
+ <div className="w-full">
998
+ <div className="bg-as-surface-secondary relative mb-4 grid h-10 grid-cols-2 rounded-xl">
999
+ <div
1000
+ className={cn(
1001
+ "bg-as-brand absolute bottom-0 left-0 top-0 z-0 rounded-xl transition-transform duration-100",
1002
+ "h-full w-1/2",
1003
+ activeTab === "fiat" ? "translate-x-full" : "translate-x-0",
1004
+ )}
1005
+ style={{ willChange: "transform" }}
1006
+ />
1007
+ <button
1008
+ className={cn(
1009
+ "relative z-10 h-full w-full rounded-xl px-3 text-sm font-medium transition-colors duration-100",
1010
+ activeTab === "crypto" ? "text-white" : "text-as-primary/70 hover:bg-as-on-surface-2 bg-transparent",
1011
+ )}
1012
+ onClick={() => {
1013
+ setActiveTab("crypto");
1014
+ setSelectedPaymentMethod(PaymentMethod.NONE); // Reset payment method when switching to crypto
1015
+ setSelectedFiatPaymentMethod(FiatPaymentMethod.NONE); // Reset fiat payment method when switching to crypto
1016
+ }}
1017
+ >
1018
+ Pay with crypto
1019
+ </button>
1020
+ <button
1021
+ className={cn(
1022
+ "relative z-10 h-full w-full rounded-xl px-3 text-sm font-medium transition-colors duration-100",
1023
+ activeTab === "fiat" ? "text-white" : "text-as-primary/70 hover:bg-as-on-surface-2 bg-transparent",
1024
+ )}
1025
+ onClick={() => {
1026
+ setActiveTab("fiat");
1027
+ setSelectedPaymentMethod(PaymentMethod.NONE); // Reset crypto payment method when switching to fiat
1028
+ setSelectedFiatPaymentMethod(FiatPaymentMethod.NONE); // Reset fiat payment method when switching to fiat
1029
+ }}
1030
+ >
1031
+ Pay with Fiat
1032
+ </button>
1033
+ </div>
820
1034
  </div>
821
1035
 
822
1036
  {/* {selectedSrcChainId === base.id || selectedDstChainId === base.id || activeTab === "fiat" ? (
@@ -827,25 +1041,47 @@ export function AnySpend({
827
1041
  </>
828
1042
  ) : null} */}
829
1043
 
830
- <div className="relative flex max-w-[calc(100vw-32px)] flex-col gap-2">
1044
+ <div className="relative flex w-full max-w-[calc(100vw-32px)] flex-col gap-2">
831
1045
  {/* Send section */}
832
1046
  {activeTab === "crypto" ? (
833
1047
  <motion.div
834
1048
  initial={{ opacity: 0, y: 20, filter: "blur(10px)" }}
835
1049
  animate={{ opacity: 1, y: 0, filter: "blur(0px)" }}
836
1050
  transition={{ duration: 0.3, delay: 0, ease: "easeInOut" }}
837
- className="bg-as-on-surface-1 relative flex w-full flex-col gap-2 rounded-2xl p-4 sm:p-6"
1051
+ className="bg-as-surface-secondary border-as-border-secondary relative flex w-full flex-col gap-2 rounded-2xl border p-4 sm:p-6"
838
1052
  >
839
1053
  <div className="flex items-center justify-between">
840
- <div className="text-as-primary/50 flex h-7 items-center text-sm">Send</div>
841
- <TokenBalance
842
- token={selectedSrcToken}
843
- walletAddress={globalAddress}
844
- onChangeInput={value => {
845
- setIsSrcInputDirty(true);
846
- setSrcAmount(value);
847
- }}
848
- />
1054
+ <div className="text-as-primary/50 flex h-7 items-center text-sm">Pay</div>
1055
+ <button
1056
+ className="text-as-primary/50 hover:text-as-primary/70 flex h-7 items-center gap-1 text-sm transition-colors"
1057
+ onClick={() => setActivePanel(PanelView.CRYPTO_PAYMENT_METHOD)}
1058
+ >
1059
+ {selectedPaymentMethod === PaymentMethod.CONNECT_WALLET ? (
1060
+ <>
1061
+ {globalAddress || wagmiAccount.address ? (
1062
+ <>
1063
+ {globalWallet?.meta?.icon && (
1064
+ <img src={globalWallet.meta.icon} alt="Connected Wallet" className="h-4 w-4 rounded-full" />
1065
+ )}
1066
+ <span>{shortenAddress(globalAddress || wagmiAccount.address || "")}</span>
1067
+ </>
1068
+ ) : (
1069
+ "Connect wallet"
1070
+ )}
1071
+ <ChevronRightCircle className="h-4 w-4" />
1072
+ </>
1073
+ ) : selectedPaymentMethod === PaymentMethod.TRANSFER_CRYPTO ? (
1074
+ <>
1075
+ Transfer crypto
1076
+ <ChevronRightCircle className="h-4 w-4" />
1077
+ </>
1078
+ ) : (
1079
+ <>
1080
+ Select payment method
1081
+ <ChevronRightCircle className="h-4 w-4" />
1082
+ </>
1083
+ )}
1084
+ </button>
849
1085
  </div>
850
1086
  <OrderTokenAmount
851
1087
  address={globalAddress}
@@ -860,8 +1096,18 @@ export function AnySpend({
860
1096
  token={selectedSrcToken}
861
1097
  setToken={setSelectedSrcToken}
862
1098
  />
863
- <div className="text-as-primary/50 flex h-5 items-center text-sm">
864
- {formatDisplayNumber(anyspendQuote?.data?.currencyIn?.amountUsd, { style: "currency", fallback: "" })}
1099
+ <div className="flex items-center justify-between">
1100
+ <div className="text-as-primary/50 flex h-5 items-center text-sm">
1101
+ {formatDisplayNumber(anyspendQuote?.data?.currencyIn?.amountUsd, { style: "currency", fallback: "" })}
1102
+ </div>
1103
+ <TokenBalance
1104
+ token={selectedSrcToken}
1105
+ walletAddress={globalAddress}
1106
+ onChangeInput={value => {
1107
+ setIsSrcInputDirty(true);
1108
+ setSrcAmount(value);
1109
+ }}
1110
+ />
865
1111
  </div>
866
1112
  </motion.div>
867
1113
  ) : (
@@ -870,7 +1116,18 @@ export function AnySpend({
870
1116
  animate={{ opacity: 1, y: 0, filter: "blur(0px)" }}
871
1117
  transition={{ duration: 0.3, delay: 0, ease: "easeInOut" }}
872
1118
  >
873
- <PanelOnramp srcAmountOnRamp={srcAmountOnRamp} setSrcAmountOnRamp={setSrcAmountOnRamp} />
1119
+ <PanelOnramp
1120
+ srcAmountOnRamp={srcAmountOnRamp}
1121
+ setSrcAmountOnRamp={setSrcAmountOnRamp}
1122
+ selectedPaymentMethod={selectedFiatPaymentMethod}
1123
+ setActivePanel={setActivePanel}
1124
+ _recipientAddress={recipientAddress}
1125
+ destinationToken={selectedDstToken}
1126
+ destinationChainId={selectedDstChainId}
1127
+ destinationAmount={dstAmount}
1128
+ onDestinationTokenChange={setSelectedDstToken}
1129
+ onDestinationChainChange={setSelectedDstChainId}
1130
+ />
874
1131
  </motion.div>
875
1132
  )}
876
1133
 
@@ -878,8 +1135,9 @@ export function AnySpend({
878
1135
  <Button
879
1136
  variant="ghost"
880
1137
  className={cn(
881
- "bg-as-n-8 border-as-stroke absolute left-1/2 top-1/2 z-10 h-10 w-10 -translate-x-1/2 -translate-y-1/2 rounded-2xl border-2 sm:h-8 sm:w-8 sm:rounded-xl",
882
- (activeTab === "fiat" || isBuyMode) && "top-[calc(50%+56px)] cursor-default",
1138
+ "border-as-stroke bg-as-surface-primary absolute left-1/2 top-1/2 z-10 h-10 w-10 -translate-x-1/2 -translate-y-1/2 rounded-xl border-2 sm:h-8 sm:w-8 sm:rounded-xl",
1139
+ isBuyMode && "top-[calc(50%+56px)] cursor-default",
1140
+ activeTab === "fiat" && "hidden",
883
1141
  )}
884
1142
  onClick={() => {
885
1143
  if (activeTab === "fiat" || isBuyMode) {
@@ -910,106 +1168,104 @@ export function AnySpend({
910
1168
  </div>
911
1169
  </Button>
912
1170
 
913
- {/* Receive section */}
914
- <motion.div
915
- initial={{ opacity: 0, y: 20, filter: "blur(10px)" }}
916
- animate={{ opacity: 1, y: 0, filter: "blur(0px)" }}
917
- transition={{ duration: 0.3, delay: 0.1, ease: "easeInOut" }}
918
- className="bg-as-on-surface-1 relative flex w-full flex-col gap-2 rounded-2xl p-4 sm:p-6"
919
- >
920
- <div className="flex w-full items-center justify-between">
921
- <div className="text-as-primary/50 flex h-7 items-center text-sm">Receive</div>
922
- {recipientAddress ? (
923
- <button
924
- className={cn(
925
- "text-as-primary/50 flex h-7 items-center gap-1 rounded-lg px-2",
926
- globalAddress && recipientAddress === globalAddress
927
- ? "bg-as-on-surface-2 hover:bg-as-on-surface-3"
928
- : "bg-as-yellow/70 hover:bg-as-yellow text-as-primary",
929
- )}
930
- onClick={() => setIsOpenPasteRecipientAddressModal(true)}
931
- >
932
- {globalAddress && recipientAddress === globalAddress && globalWallet?.meta?.icon ? (
933
- <img
934
- src={globalWallet?.meta?.icon}
935
- alt="Current wallet"
936
- className="bg-as-primary h-4 w-4 rounded-full"
937
- />
938
- ) : (
939
- <ClipboardIcon className="h-4 w-4" />
940
- )}
941
-
942
- <div className="text-sm">{recipientName ? recipientName : shortenAddress(recipientAddress)}</div>
943
- <ChevronsUpDown className="h-3 w-3" />
944
- </button>
1171
+ {/* Receive section - Hidden when fiat tab is active */}
1172
+ {activeTab !== "fiat" && (
1173
+ <motion.div
1174
+ initial={{ opacity: 0, y: 20, filter: "blur(10px)" }}
1175
+ animate={{ opacity: 1, y: 0, filter: "blur(0px)" }}
1176
+ transition={{ duration: 0.3, delay: 0.1, ease: "easeInOut" }}
1177
+ className="bg-as-surface-secondary border-as-border-secondary relative flex w-full flex-col gap-2 rounded-2xl border p-4 sm:p-6"
1178
+ >
1179
+ <div className="flex w-full items-center justify-between">
1180
+ <div className="text-as-primary/50 flex h-7 items-center text-sm">Receive</div>
1181
+ {recipientAddress ? (
1182
+ <button
1183
+ className={cn("text-as-primary/70 flex h-7 items-center gap-2 rounded-lg px-2")}
1184
+ onClick={() => setActivePanel(PanelView.RECIPIENT_SELECTION)}
1185
+ >
1186
+ {globalAddress && recipientAddress === globalAddress && globalWallet?.meta?.icon ? (
1187
+ <img
1188
+ src={globalWallet?.meta?.icon}
1189
+ alt="Current wallet"
1190
+ className="bg-as-primary h-6 w-6 rounded-full"
1191
+ />
1192
+ ) : (
1193
+ <div className="flex h-6 w-6 items-center justify-center rounded-full bg-orange-500 text-xs text-white">
1194
+ 🦊
1195
+ </div>
1196
+ )}
1197
+ <div className="text-sm">{recipientName ? recipientName : shortenAddress(recipientAddress)}</div>
1198
+ <ChevronRightCircle className="h-4 w-4" />
1199
+ </button>
1200
+ ) : (
1201
+ <button
1202
+ className="text-as-primary/70 flex items-center gap-1 rounded-lg"
1203
+ onClick={() => setActivePanel(PanelView.RECIPIENT_SELECTION)}
1204
+ >
1205
+ <div className="text-sm font-medium">Select recipient</div>
1206
+ <ChevronsUpDown className="h-3 w-3" />
1207
+ </button>
1208
+ )}
1209
+ </div>
1210
+ {isBuyMode ? (
1211
+ // Fixed destination token display in buy mode
1212
+ <div className="flex items-center justify-between">
1213
+ <div className="text-as-primary text-2xl font-bold">{dstAmount || "0"}</div>
1214
+ <div className="bg-as-brand/10 border-as-brand/30 flex items-center gap-3 rounded-xl border px-4 py-3">
1215
+ {selectedDstToken.metadata?.logoURI && (
1216
+ <img
1217
+ src={selectedDstToken.metadata.logoURI}
1218
+ alt={selectedDstToken.symbol}
1219
+ className="h-8 w-8 rounded-full"
1220
+ />
1221
+ )}
1222
+ <span className="text-as-brand text-lg font-bold">{selectedDstToken.symbol}</span>
1223
+ </div>
1224
+ </div>
945
1225
  ) : (
946
- <button
947
- className="text-as-primary/70 flex items-center gap-1 rounded-lg"
948
- onClick={() => setIsOpenPasteRecipientAddressModal(true)}
949
- >
950
- <div className="text-sm font-medium">Select recipient</div>
951
- <ChevronsUpDown className="h-3 w-3" />
952
- </button>
1226
+ <OrderTokenAmount
1227
+ address={recipientAddress}
1228
+ context="to"
1229
+ inputValue={dstAmount}
1230
+ onChangeInput={value => {
1231
+ setIsSrcInputDirty(false);
1232
+ setDstAmount(value);
1233
+ }}
1234
+ chainId={selectedDstChainId}
1235
+ setChainId={setSelectedDstChainId}
1236
+ token={selectedDstToken}
1237
+ setToken={setSelectedDstToken}
1238
+ />
953
1239
  )}
954
- </div>
955
- {isBuyMode ? (
956
- // Fixed destination token display in buy mode
957
- <div className="flex items-center justify-between">
958
- <div className="text-as-primary text-2xl font-bold">{dstAmount || "0"}</div>
959
- <div className="bg-as-brand/10 border-as-brand/30 flex items-center gap-3 rounded-xl border px-4 py-3">
960
- {selectedDstToken.metadata?.logoURI && (
961
- <img
962
- src={selectedDstToken.metadata.logoURI}
963
- alt={selectedDstToken.symbol}
964
- className="h-8 w-8 rounded-full"
965
- />
966
- )}
967
- <span className="text-as-brand text-lg font-bold">{selectedDstToken.symbol}</span>
968
- </div>
1240
+ <div className="text-as-primary/50 flex h-5 items-center text-sm">
1241
+ {formatDisplayNumber(anyspendQuote?.data?.currencyOut?.amountUsd, { style: "currency", fallback: "" })}
1242
+ {anyspendQuote?.data?.currencyIn?.amountUsd &&
1243
+ anyspendQuote?.data?.currencyOut?.amountUsd &&
1244
+ (() => {
1245
+ const { percentage, isNegative } = calculatePriceImpact(
1246
+ anyspendQuote.data.currencyIn.amountUsd,
1247
+ anyspendQuote.data.currencyOut.amountUsd,
1248
+ );
1249
+
1250
+ // Parse the percentage as a number for comparison
1251
+ const percentageNum = parseFloat(percentage);
1252
+
1253
+ // Don't show if less than 1%
1254
+ if (percentageNum < 1) {
1255
+ return null;
1256
+ }
1257
+
1258
+ // Using inline style to ensure color displays
1259
+ return (
1260
+ <span className="ml-2" style={{ color: percentageNum >= 10 ? "red" : "#FFD700" }}>
1261
+ ({isNegative ? "-" : ""}
1262
+ {percentage}%)
1263
+ </span>
1264
+ );
1265
+ })()}
969
1266
  </div>
970
- ) : (
971
- <OrderTokenAmount
972
- address={recipientAddress}
973
- context="to"
974
- inputValue={dstAmount}
975
- onChangeInput={value => {
976
- setIsSrcInputDirty(false);
977
- setDstAmount(value);
978
- }}
979
- chainId={selectedDstChainId}
980
- setChainId={setSelectedDstChainId}
981
- token={selectedDstToken}
982
- setToken={setSelectedDstToken}
983
- />
984
- )}
985
- <div className="text-as-primary/50 flex h-5 items-center text-sm">
986
- {formatDisplayNumber(anyspendQuote?.data?.currencyOut?.amountUsd, { style: "currency", fallback: "" })}
987
- {anyspendQuote?.data?.currencyIn?.amountUsd &&
988
- anyspendQuote?.data?.currencyOut?.amountUsd &&
989
- (() => {
990
- const { percentage, isNegative } = calculatePriceImpact(
991
- anyspendQuote.data.currencyIn.amountUsd,
992
- anyspendQuote.data.currencyOut.amountUsd,
993
- );
994
-
995
- // Parse the percentage as a number for comparison
996
- const percentageNum = parseFloat(percentage);
997
-
998
- // Don't show if less than 1%
999
- if (percentageNum < 1) {
1000
- return null;
1001
- }
1002
-
1003
- // Using inline style to ensure color displays
1004
- return (
1005
- <span className="ml-2" style={{ color: percentageNum >= 10 ? "red" : "#FFD700" }}>
1006
- ({isNegative ? "-" : ""}
1007
- {percentage}%)
1008
- </span>
1009
- );
1010
- })()}
1011
- </div>
1012
- </motion.div>
1267
+ </motion.div>
1268
+ )}
1013
1269
  </div>
1014
1270
 
1015
1271
  {/* Order details section */}
@@ -1046,7 +1302,7 @@ export function AnySpend({
1046
1302
  initial={{ opacity: 0, y: 20, filter: "blur(10px)" }}
1047
1303
  animate={{ opacity: 1, y: 0, filter: "blur(0px)" }}
1048
1304
  transition={{ duration: 0.3, delay: 0.2, ease: "easeInOut" }}
1049
- className="flex w-full max-w-[460px] flex-col gap-2"
1305
+ className="mt-4 flex w-full max-w-[460px] flex-col gap-2 pb-2"
1050
1306
  >
1051
1307
  <ShinyButton
1052
1308
  accentColor={"hsl(var(--as-brand))"}
@@ -1072,9 +1328,7 @@ export function AnySpend({
1072
1328
  >
1073
1329
  <HistoryIcon className="h-4 w-4" /> <span className="pr-4">Transaction History</span>
1074
1330
  </Button>
1075
- ) : (
1076
- <div className="h-2 w-full" />
1077
- )}
1331
+ ) : null}
1078
1332
  </motion.div>
1079
1333
  </div>
1080
1334
  );
@@ -1096,9 +1350,15 @@ export function AnySpend({
1096
1350
  onOrderCreated={orderId => {
1097
1351
  setOrderId(orderId);
1098
1352
  setActivePanel(PanelView.ORDER_DETAILS);
1099
- // Add orderId to URL for persistence
1100
- const params = new URLSearchParams();
1353
+ // Add orderId and payment method to URL for persistence
1354
+ const params = new URLSearchParams(searchParams.toString()); // Preserve existing params
1101
1355
  params.set("orderId", orderId);
1356
+ // For fiat payments, the payment method is always fiat (but we use the active tab context)
1357
+ if (activeTab === "fiat") {
1358
+ params.set("paymentMethod", "fiat");
1359
+ } else if (selectedPaymentMethod !== PaymentMethod.NONE) {
1360
+ params.set("paymentMethod", selectedPaymentMethod);
1361
+ }
1102
1362
  router.push(`${window.location.pathname}?${params.toString()}`);
1103
1363
  }}
1104
1364
  onBack={() => setActivePanel(PanelView.MAIN)}
@@ -1107,10 +1367,111 @@ export function AnySpend({
1107
1367
  />
1108
1368
  );
1109
1369
 
1370
+ const recipientSelectionView = (
1371
+ <div className="mx-auto w-[460px] max-w-full">
1372
+ <div className="flex flex-col gap-6">
1373
+ {/* Header */}
1374
+ <div className="flex justify-around">
1375
+ <button
1376
+ onClick={() => setActivePanel(PanelView.MAIN)}
1377
+ className="text-as-quaternary hover:text-as-primary flex h-8 w-8 items-center justify-center rounded-lg transition-colors"
1378
+ >
1379
+ <ChevronLeft className="h-6 w-6" />
1380
+ </button>
1381
+ <div className="flex-1 text-center">
1382
+ <h2 className="text-as-primary text-lg font-semibold">Add recipient address or ENS</h2>
1383
+ <p className="text-as-primary/60 text-sm">Swap and send tokens to another address</p>
1384
+ </div>
1385
+ </div>
1386
+
1387
+ {/* Address Input */}
1388
+ <div className="flex flex-col gap-4">
1389
+ <div className="bg-as-surface-secondary border-as-border-secondary flex h-12 w-full overflow-hidden rounded-xl border">
1390
+ <input
1391
+ type="text"
1392
+ placeholder="Enter recipient address"
1393
+ value={recipientAddress || ""}
1394
+ onChange={e => setRecipientAddress(e.target.value)}
1395
+ onKeyDown={e => {
1396
+ if (e.key === "Enter" && recipientAddress) {
1397
+ setActivePanel(PanelView.MAIN);
1398
+ }
1399
+ }}
1400
+ className="text-as-primary placeholder:text-as-primary/50 flex-1 bg-transparent px-4 text-base focus:outline-none"
1401
+ autoFocus
1402
+ />
1403
+ <div className="border-as-border-secondary border-l">
1404
+ <button
1405
+ onClick={async () => {
1406
+ try {
1407
+ const text = await navigator.clipboard.readText();
1408
+ setRecipientAddress(text);
1409
+ } catch (err) {
1410
+ console.error("Failed to read clipboard:", err);
1411
+ }
1412
+ }}
1413
+ className="text-as-primary/70 hover:text-as-primary hover:bg-as-surface-primary h-full px-4 font-semibold transition-colors"
1414
+ >
1415
+ Paste
1416
+ </button>
1417
+ </div>
1418
+ </div>
1419
+
1420
+ {/* Confirm Button */}
1421
+ <button
1422
+ onClick={() => {
1423
+ if (recipientAddress) {
1424
+ setActivePanel(PanelView.MAIN);
1425
+ }
1426
+ }}
1427
+ disabled={!recipientAddress}
1428
+ className="bg-as-brand hover:bg-as-brand/90 disabled:bg-as-on-surface-2 disabled:text-as-secondary h-12 w-full rounded-xl font-medium text-white transition-colors disabled:cursor-not-allowed"
1429
+ >
1430
+ Confirm recipient address
1431
+ </button>
1432
+ </div>
1433
+ </div>
1434
+ </div>
1435
+ );
1436
+
1437
+ const cryptoPaymentMethodView = (
1438
+ <CryptoPaymentMethod
1439
+ globalAddress={globalAddress}
1440
+ globalWallet={globalWallet}
1441
+ selectedPaymentMethod={selectedPaymentMethod}
1442
+ setSelectedPaymentMethod={setSelectedPaymentMethod}
1443
+ isCreatingOrder={isCreatingOrder}
1444
+ onBack={() => setActivePanel(PanelView.MAIN)}
1445
+ onSelectPaymentMethod={(method: PaymentMethod) => {
1446
+ setSelectedPaymentMethod(method);
1447
+ setActivePanel(PanelView.MAIN);
1448
+ }}
1449
+ />
1450
+ );
1451
+
1452
+ const fiatPaymentMethodView = (
1453
+ <FiatPaymentMethodComponent
1454
+ selectedPaymentMethod={selectedFiatPaymentMethod}
1455
+ setSelectedPaymentMethod={setSelectedFiatPaymentMethod}
1456
+ onBack={() => setActivePanel(PanelView.MAIN)}
1457
+ onSelectPaymentMethod={(method: FiatPaymentMethod) => {
1458
+ setSelectedFiatPaymentMethod(method);
1459
+ setActivePanel(PanelView.MAIN); // Go back to main panel to show updated pricing
1460
+ }}
1461
+ srcAmountOnRamp={srcAmountOnRamp}
1462
+ isMainnet={isMainnet}
1463
+ />
1464
+ );
1465
+
1110
1466
  // Add tabs to the main component when no order is loaded
1111
1467
  return (
1112
1468
  <StyleRoot>
1113
- <div className={cn("mx-auto max-w-[calc(100vw-32px)]")}>
1469
+ <div
1470
+ className={cn(
1471
+ "mx-auto w-full max-w-[460px]",
1472
+ mode === "page" && "bg-as-surface-primary border-as-border-secondary rounded-2xl border p-6 shadow-xl",
1473
+ )}
1474
+ >
1114
1475
  <TransitionPanel
1115
1476
  activeIndex={
1116
1477
  orderId
@@ -1121,7 +1482,7 @@ export function AnySpend({
1121
1482
  ? PanelView.MAIN
1122
1483
  : activePanel
1123
1484
  }
1124
- className={cn("w-full", {
1485
+ className={cn("overflow-hidden", {
1125
1486
  "mt-0": mode === "modal",
1126
1487
  })}
1127
1488
  variants={{
@@ -1137,14 +1498,11 @@ export function AnySpend({
1137
1498
  <div key="order-details-view">{orderDetailsView}</div>,
1138
1499
  <div key="loading-view">{OrderDetailsLoadingView}</div>,
1139
1500
  <div key="fiat-payment-view">{onrampPaymentView}</div>,
1501
+ <div key="recipient-selection-view">{recipientSelectionView}</div>,
1502
+ <div key="crypto-payment-method-view">{cryptoPaymentMethodView}</div>,
1503
+ <div key="fiat-payment-method-view">{fiatPaymentMethodView}</div>,
1140
1504
  ]}
1141
1505
  </TransitionPanel>
1142
- <EnterRecipientModal
1143
- isOpenPasteRecipientAddress={isOpenPasteRecipientAddressModal}
1144
- setIsOpenPasteRecipientAddress={setIsOpenPasteRecipientAddressModal}
1145
- recipientAddress={recipientAddress}
1146
- setRecipientAddress={setRecipientAddress}
1147
- />
1148
1506
  </div>
1149
1507
  </StyleRoot>
1150
1508
  );