@b3dotfun/sdk 0.0.67 → 0.0.68-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 (40) hide show
  1. package/dist/cjs/anyspend/react/components/AnySpend.js +38 -26
  2. package/dist/cjs/anyspend/react/components/AnySpendCustom.js +10 -6
  3. package/dist/cjs/anyspend/react/components/AnySpendCustomExactIn.js +1 -1
  4. package/dist/cjs/anyspend/react/components/common/CryptoPaymentMethod.js +0 -7
  5. package/dist/cjs/anyspend/react/components/common/CryptoReceiveSection.d.ts +2 -7
  6. package/dist/cjs/anyspend/react/components/common/CryptoReceiveSection.js +3 -13
  7. package/dist/cjs/anyspend/react/hooks/index.d.ts +1 -0
  8. package/dist/cjs/anyspend/react/hooks/index.js +1 -0
  9. package/dist/cjs/anyspend/react/hooks/useAnyspendFlow.d.ts +1 -1
  10. package/dist/cjs/anyspend/react/hooks/useAnyspendFlow.js +12 -12
  11. package/dist/cjs/anyspend/react/hooks/useConnectedWalletDisplay.js +2 -1
  12. package/dist/cjs/anyspend/react/hooks/useRecipientAddressState.d.ts +52 -0
  13. package/dist/cjs/anyspend/react/hooks/useRecipientAddressState.js +52 -0
  14. package/dist/esm/anyspend/react/components/AnySpend.js +38 -26
  15. package/dist/esm/anyspend/react/components/AnySpendCustom.js +10 -6
  16. package/dist/esm/anyspend/react/components/AnySpendCustomExactIn.js +1 -1
  17. package/dist/esm/anyspend/react/components/common/CryptoPaymentMethod.js +1 -8
  18. package/dist/esm/anyspend/react/components/common/CryptoReceiveSection.d.ts +2 -7
  19. package/dist/esm/anyspend/react/components/common/CryptoReceiveSection.js +3 -13
  20. package/dist/esm/anyspend/react/hooks/index.d.ts +1 -0
  21. package/dist/esm/anyspend/react/hooks/index.js +1 -0
  22. package/dist/esm/anyspend/react/hooks/useAnyspendFlow.d.ts +1 -1
  23. package/dist/esm/anyspend/react/hooks/useAnyspendFlow.js +12 -12
  24. package/dist/esm/anyspend/react/hooks/useConnectedWalletDisplay.js +2 -1
  25. package/dist/esm/anyspend/react/hooks/useRecipientAddressState.d.ts +52 -0
  26. package/dist/esm/anyspend/react/hooks/useRecipientAddressState.js +49 -0
  27. package/dist/types/anyspend/react/components/common/CryptoReceiveSection.d.ts +2 -7
  28. package/dist/types/anyspend/react/hooks/index.d.ts +1 -0
  29. package/dist/types/anyspend/react/hooks/useAnyspendFlow.d.ts +1 -1
  30. package/dist/types/anyspend/react/hooks/useRecipientAddressState.d.ts +52 -0
  31. package/package.json +3 -3
  32. package/src/anyspend/react/components/AnySpend.tsx +43 -31
  33. package/src/anyspend/react/components/AnySpendCustom.tsx +10 -7
  34. package/src/anyspend/react/components/AnySpendCustomExactIn.tsx +1 -1
  35. package/src/anyspend/react/components/common/CryptoPaymentMethod.tsx +1 -8
  36. package/src/anyspend/react/components/common/CryptoReceiveSection.tsx +5 -27
  37. package/src/anyspend/react/hooks/index.ts +1 -0
  38. package/src/anyspend/react/hooks/useAnyspendFlow.ts +13 -12
  39. package/src/anyspend/react/hooks/useConnectedWalletDisplay.ts +4 -1
  40. package/src/anyspend/react/hooks/useRecipientAddressState.ts +78 -0
@@ -14,7 +14,9 @@ import { parseUnits } from "viem";
14
14
  import { base, mainnet } from "viem/chains";
15
15
  import { useAutoSelectCryptoPaymentMethod } from "../hooks/useAutoSelectCryptoPaymentMethod.js";
16
16
  import { useAutoSetActiveWalletFromWagmi } from "../hooks/useAutoSetActiveWalletFromWagmi.js";
17
+ import { useConnectedWalletDisplay } from "../hooks/useConnectedWalletDisplay.js";
17
18
  import { useCryptoPaymentMethodState } from "../hooks/useCryptoPaymentMethodState.js";
19
+ import { useRecipientAddressState } from "../hooks/useRecipientAddressState.js";
18
20
  import { AnySpendFingerprintWrapper, getFingerprintConfig } from "./AnySpendFingerprintWrapper.js";
19
21
  import { CryptoPaymentMethod, CryptoPaymentMethodType } from "./common/CryptoPaymentMethod.js";
20
22
  import { CryptoPaySection } from "./common/CryptoPaySection.js";
@@ -304,13 +306,22 @@ function AnySpendInner({ destinationTokenAddress, destinationTokenChainId, mode
304
306
  // : isAddress(newRecipientAddress) || (resolvedAddress && isAddress(resolvedAddress)),
305
307
  // [selectedDstChainId, newRecipientAddress, resolvedAddress]
306
308
  // );
307
- // State for recipient selection
308
- const [recipientAddress, setRecipientAddress] = useState();
309
309
  const { address: globalAddress, wallet: globalWallet, connectedEOAWallet } = useAccountWallet();
310
- const recipientProfile = useProfile({ address: recipientAddress, fresh: true });
311
- const recipientName = recipientProfile.data?.name;
312
310
  // Auto-set active wallet from wagmi
313
311
  useAutoSetActiveWalletFromWagmi();
312
+ // Get wallet address based on selected payment method
313
+ const { walletAddress } = useConnectedWalletDisplay(effectiveCryptoPaymentMethod);
314
+ // Recipient address state with dual-state system (auto + explicit user selection)
315
+ // The hook automatically manages priority: props > user selection > wallet/global
316
+ const { setSelectedRecipientAddress, effectiveRecipientAddress,
317
+ // resetRecipientAddress, // Not used yet, but available for future use
318
+ } = useRecipientAddressState({
319
+ recipientAddressFromProps,
320
+ walletAddress,
321
+ globalAddress,
322
+ });
323
+ const recipientProfile = useProfile({ address: effectiveRecipientAddress, fresh: true });
324
+ const recipientName = recipientProfile.data?.name;
314
325
  // Check token balance for crypto payments
315
326
  const { rawBalance, isLoading: isBalanceLoading } = useTokenBalanceDirect({
316
327
  token: selectedSrcToken,
@@ -368,7 +379,7 @@ function AnySpendInner({ destinationTokenAddress, destinationTokenChainId, mode
368
379
  type: "swap",
369
380
  tradeType: isSrcInputDirty ? "EXACT_INPUT" : "EXACT_OUTPUT",
370
381
  amount: activeInputAmountInWei,
371
- recipientAddress,
382
+ recipientAddress: effectiveRecipientAddress,
372
383
  }
373
384
  : {
374
385
  srcChain: base.id,
@@ -378,7 +389,7 @@ function AnySpendInner({ destinationTokenAddress, destinationTokenChainId, mode
378
389
  type: "swap",
379
390
  tradeType: "EXACT_INPUT",
380
391
  amount: srcAmountOnrampInWei,
381
- recipientAddress,
392
+ recipientAddress: effectiveRecipientAddress,
382
393
  onrampVendor: getOnrampVendor(selectedFiatPaymentMethod),
383
394
  });
384
395
  // Load custom recipients from local storage on mount
@@ -394,8 +405,8 @@ function AnySpendInner({ destinationTokenAddress, destinationTokenChainId, mode
394
405
  // Add custom recipients based on local storage
395
406
  setCustomRecipients(parsedRecipients);
396
407
  // If no wallet is connected and no recipient is selected, select the first recipient
397
- if (!globalAddress && !recipientAddress && parsedRecipients.length > 0) {
398
- setRecipientAddress(parsedRecipients[0].address);
408
+ if (!globalAddress && !effectiveRecipientAddress && parsedRecipients.length > 0) {
409
+ setSelectedRecipientAddress(parsedRecipients[0].address);
399
410
  }
400
411
  }
401
412
  }
@@ -403,7 +414,7 @@ function AnySpendInner({ destinationTokenAddress, destinationTokenChainId, mode
403
414
  console.error("Error loading recipients from local storage:", err);
404
415
  }
405
416
  // Only run this effect once on mount
406
- }, [globalAddress, recipientAddress, customRecipients.length]);
417
+ }, [globalAddress, effectiveRecipientAddress, customRecipients.length, setSelectedRecipientAddress]);
407
418
  // Update dependent amount when relay price changes
408
419
  useEffect(() => {
409
420
  if (anyspendQuote?.data &&
@@ -506,7 +517,7 @@ function AnySpendInner({ destinationTokenAddress, destinationTokenChainId, mode
506
517
  return { text: "No quote found", disable: true, error: false, loading: false };
507
518
  if (activeTab === "fiat") {
508
519
  // For fiat: check recipient first, then payment method
509
- if (!recipientAddress)
520
+ if (!effectiveRecipientAddress)
510
521
  return { text: "Select recipient", disable: false, error: false, loading: false };
511
522
  // If no fiat payment method selected, show "Select payment method"
512
523
  if (selectedFiatPaymentMethod === FiatPaymentMethod.NONE) {
@@ -522,7 +533,7 @@ function AnySpendInner({ destinationTokenAddress, destinationTokenChainId, mode
522
533
  return { text: "Choose payment method", disable: false, error: false, loading: false };
523
534
  }
524
535
  // Check recipient after payment method
525
- if (!recipientAddress)
536
+ if (!effectiveRecipientAddress)
526
537
  return { text: "Select recipient", disable: false, error: false, loading: false };
527
538
  // If payment method selected, show appropriate action
528
539
  if (effectiveCryptoPaymentMethod === CryptoPaymentMethodType.CONNECT_WALLET ||
@@ -538,7 +549,7 @@ function AnySpendInner({ destinationTokenAddress, destinationTokenChainId, mode
538
549
  activeInputAmountInWei,
539
550
  isSameChainSameToken,
540
551
  isLoadingAnyspendQuote,
541
- recipientAddress,
552
+ effectiveRecipientAddress,
542
553
  isCreatingOrder,
543
554
  isCreatingOnrampOrder,
544
555
  anyspendQuote,
@@ -554,11 +565,11 @@ function AnySpendInner({ destinationTokenAddress, destinationTokenChainId, mode
554
565
  invariant(anyspendQuote, "Relay price is not found");
555
566
  if (activeTab === "fiat") {
556
567
  // For fiat: check recipient first
557
- if (!recipientAddress) {
568
+ if (!effectiveRecipientAddress) {
558
569
  navigateToPanel(PanelView.RECIPIENT_SELECTION, "forward");
559
570
  return;
560
571
  }
561
- invariant(recipientAddress, "Recipient address is not found");
572
+ invariant(effectiveRecipientAddress, "Recipient address is not found");
562
573
  // If no fiat payment method selected, show payment method selection
563
574
  if (selectedFiatPaymentMethod === FiatPaymentMethod.NONE) {
564
575
  navigateToPanel(PanelView.FIAT_PAYMENT_METHOD, "forward");
@@ -577,11 +588,11 @@ function AnySpendInner({ destinationTokenAddress, destinationTokenChainId, mode
577
588
  return;
578
589
  }
579
590
  // Check recipient after payment method
580
- if (!recipientAddress) {
591
+ if (!effectiveRecipientAddress) {
581
592
  navigateToPanel(PanelView.RECIPIENT_SELECTION, "forward");
582
593
  return;
583
594
  }
584
- invariant(recipientAddress, "Recipient address is not found");
595
+ invariant(effectiveRecipientAddress, "Recipient address is not found");
585
596
  // If payment method is selected, create order with payment method info
586
597
  if (effectiveCryptoPaymentMethod === CryptoPaymentMethodType.CONNECT_WALLET ||
587
598
  effectiveCryptoPaymentMethod === CryptoPaymentMethodType.GLOBAL_WALLET ||
@@ -610,13 +621,13 @@ function AnySpendInner({ destinationTokenAddress, destinationTokenChainId, mode
610
621
  const handleCryptoSwap = async (method) => {
611
622
  try {
612
623
  invariant(anyspendQuote, "Relay price is not found");
613
- invariant(recipientAddress, "Recipient address is not found");
624
+ invariant(effectiveRecipientAddress, "Recipient address is not found");
614
625
  // Debug: Check payment method values
615
626
  console.log("handleCryptoSwap - method parameter:", method);
616
627
  console.log("handleCryptoSwap - selectedCryptoPaymentMethod state:", selectedCryptoPaymentMethod);
617
628
  const srcAmountBigInt = parseUnits(srcAmount.replace(/,/g, ""), selectedSrcToken.decimals);
618
629
  createOrder({
619
- recipientAddress,
630
+ recipientAddress: effectiveRecipientAddress,
620
631
  orderType: "swap",
621
632
  srcChain: selectedSrcChainId,
622
633
  dstChain: isBuyMode ? destinationTokenChainId : selectedDstChainId,
@@ -638,7 +649,7 @@ function AnySpendInner({ destinationTokenAddress, destinationTokenChainId, mode
638
649
  const handleFiatOrder = async (paymentMethod) => {
639
650
  try {
640
651
  invariant(anyspendQuote, "Relay price is not found");
641
- invariant(recipientAddress, "Recipient address is not found");
652
+ invariant(effectiveRecipientAddress, "Recipient address is not found");
642
653
  if (!srcAmountOnRamp || parseFloat(srcAmountOnRamp) <= 0) {
643
654
  toast.error("Please enter a valid amount");
644
655
  return;
@@ -678,7 +689,7 @@ function AnySpendInner({ destinationTokenAddress, destinationTokenChainId, mode
678
689
  return selectedDstToken;
679
690
  };
680
691
  createOnrampOrder({
681
- recipientAddress,
692
+ recipientAddress: effectiveRecipientAddress,
682
693
  orderType: "swap",
683
694
  dstChain: getDstToken().chainId,
684
695
  dstToken: getDstToken(),
@@ -784,7 +795,7 @@ function AnySpendInner({ destinationTokenAddress, destinationTokenChainId, mode
784
795
  else {
785
796
  setActivePanel(panelIndex);
786
797
  }
787
- }, _recipientAddress: recipientAddress, destinationToken: selectedDstToken, destinationChainId: selectedDstChainId, destinationAmount: dstAmount, onDestinationTokenChange: setSelectedDstToken, onDestinationChainChange: setSelectedDstChainId, fiatPaymentMethodIndex: PanelView.FIAT_PAYMENT_METHOD, recipientSelectionPanelIndex: PanelView.RECIPIENT_SELECTION, hideDstToken: isBuyMode, anyspendQuote: anyspendQuote, onShowPointsDetail: () => navigateToPanel(PanelView.POINTS_DETAIL, "forward"), onShowFeeDetail: () => navigateToPanel(PanelView.FEE_DETAIL, "forward"), customUsdInputValues: customUsdInputValues }) })), _jsx(Button, { variant: "ghost", className: cn("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", isBuyMode && "top-[calc(50%+56px)] cursor-default", activeTab === "fiat" && "hidden"), onClick: () => {
798
+ }, _recipientAddress: effectiveRecipientAddress, destinationToken: selectedDstToken, destinationChainId: selectedDstChainId, destinationAmount: dstAmount, onDestinationTokenChange: setSelectedDstToken, onDestinationChainChange: setSelectedDstChainId, fiatPaymentMethodIndex: PanelView.FIAT_PAYMENT_METHOD, recipientSelectionPanelIndex: PanelView.RECIPIENT_SELECTION, hideDstToken: isBuyMode, anyspendQuote: anyspendQuote, onShowPointsDetail: () => navigateToPanel(PanelView.POINTS_DETAIL, "forward"), onShowFeeDetail: () => navigateToPanel(PanelView.FEE_DETAIL, "forward"), customUsdInputValues: customUsdInputValues }) })), _jsx(Button, { variant: "ghost", className: cn("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", isBuyMode && "top-[calc(50%+56px)] cursor-default", activeTab === "fiat" && "hidden"), onClick: () => {
788
799
  if (activeTab === "fiat" || isBuyMode) {
789
800
  return;
790
801
  }
@@ -803,11 +814,11 @@ function AnySpendInner({ destinationTokenAddress, destinationTokenChainId, mode
803
814
  const tempDstAmount = dstAmount;
804
815
  setSrcAmount(tempDstAmount);
805
816
  setDstAmount(tempSrcAmount);
806
- }, 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, selectedRecipientAddress: recipientAddress, recipientName: recipientName || undefined, onSelectRecipient: () => navigateToPanel(PanelView.RECIPIENT_SELECTION, "forward"), setRecipientAddress: setRecipientAddress, recipientAddressFromProps: recipientAddressFromProps, globalAddress: globalAddress, dstAmount: dstAmount, dstToken: selectedDstToken, selectedDstChainId: selectedDstChainId, setSelectedDstChainId: setSelectedDstChainId, setSelectedDstToken: setSelectedDstToken, isSrcInputDirty: isSrcInputDirty, onChangeDstAmount: value => {
817
+ }, 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, onSelectRecipient: () => navigateToPanel(PanelView.RECIPIENT_SELECTION, "forward"), dstAmount: dstAmount, dstToken: selectedDstToken, selectedDstChainId: selectedDstChainId, setSelectedDstChainId: setSelectedDstChainId, setSelectedDstToken: setSelectedDstToken, isSrcInputDirty: isSrcInputDirty, onChangeDstAmount: value => {
807
818
  setIsSrcInputDirty(false);
808
819
  setDstAmount(value);
809
- }, anyspendQuote: anyspendQuote, onShowPointsDetail: () => navigateToPanel(PanelView.POINTS_DETAIL, "forward"), onShowFeeDetail: () => navigateToPanel(PanelView.FEE_DETAIL, "forward"), selectedCryptoPaymentMethod: effectiveCryptoPaymentMethod }))] }), _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: 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 || recipientAddress) ? (_jsxs(Button, { variant: "link", onClick: onClickHistory, className: "text-as-primary/50 hover:text-as-primary flex items-center gap-1 transition-colors", children: [_jsx(HistoryIcon, { className: "h-4 w-4" }), " ", _jsx("span", { className: "pr-4", children: "Transaction History" })] })) : null] })] }));
810
- const onrampPaymentView = (_jsx(PanelOnrampPayment, { srcAmountOnRamp: srcAmountOnRamp, recipientName: recipientName || undefined, recipientAddress: recipientAddress, isBuyMode: isBuyMode, destinationTokenChainId: destinationTokenChainId, destinationTokenAddress: destinationTokenAddress, selectedDstChainId: selectedDstChainId, selectedDstToken: selectedDstToken, orderType: "swap", anyspendQuote: anyspendQuote, globalAddress: globalAddress, onOrderCreated: orderId => {
820
+ }, anyspendQuote: anyspendQuote, onShowPointsDetail: () => navigateToPanel(PanelView.POINTS_DETAIL, "forward"), onShowFeeDetail: () => navigateToPanel(PanelView.FEE_DETAIL, "forward") }))] }), _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: 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: "text-as-primary/50 hover:text-as-primary flex items-center gap-1 transition-colors", children: [_jsx(HistoryIcon, { className: "h-4 w-4" }), " ", _jsx("span", { className: "pr-4", children: "Transaction History" })] })) : null] })] }));
821
+ const onrampPaymentView = (_jsx(PanelOnrampPayment, { srcAmountOnRamp: srcAmountOnRamp, recipientName: recipientName || undefined, recipientAddress: effectiveRecipientAddress, isBuyMode: isBuyMode, destinationTokenChainId: destinationTokenChainId, destinationTokenAddress: destinationTokenAddress, selectedDstChainId: selectedDstChainId, selectedDstToken: selectedDstToken, orderType: "swap", anyspendQuote: anyspendQuote, globalAddress: globalAddress, onOrderCreated: orderId => {
811
822
  setOrderId(orderId);
812
823
  navigateToPanel(PanelView.ORDER_DETAILS, "forward");
813
824
  // Add orderId and payment method to URL for persistence
@@ -822,8 +833,9 @@ function AnySpendInner({ destinationTokenAddress, destinationTokenChainId, mode
822
833
  }
823
834
  router.push(`${window.location.pathname}?${params.toString()}`);
824
835
  }, onBack: navigateBack, recipientEnsName: globalWallet?.ensName, recipientImageUrl: globalWallet?.meta?.icon }));
825
- const recipientSelectionView = (_jsx(RecipientSelection, { initialValue: recipientAddress || "", onBack: navigateBack, onConfirm: address => {
826
- setRecipientAddress(address);
836
+ const recipientSelectionView = (_jsx(RecipientSelection, { initialValue: effectiveRecipientAddress || "", onBack: navigateBack, onConfirm: address => {
837
+ // User manually selected a recipient
838
+ setSelectedRecipientAddress(address);
827
839
  navigateBack();
828
840
  } }));
829
841
  const cryptoPaymentMethodView = (_jsx(CryptoPaymentMethod, { globalAddress: globalAddress, globalWallet: globalWallet, selectedPaymentMethod: effectiveCryptoPaymentMethod, setSelectedPaymentMethod: method => {
@@ -16,6 +16,7 @@ import { base } from "viem/chains";
16
16
  import { useFeatureFlags } from "../contexts/FeatureFlagsContext.js";
17
17
  import { useAutoSetActiveWalletFromWagmi } from "../hooks/useAutoSetActiveWalletFromWagmi.js";
18
18
  import { useCryptoPaymentMethodState } from "../hooks/useCryptoPaymentMethodState.js";
19
+ import { useRecipientAddressState } from "../hooks/useRecipientAddressState.js";
19
20
  import { AnySpendFingerprintWrapper, getFingerprintConfig } from "./AnySpendFingerprintWrapper.js";
20
21
  import { CryptoPaymentMethod, CryptoPaymentMethodType } from "./common/CryptoPaymentMethod.js";
21
22
  import { FeeBreakDown } from "./common/FeeBreakDown.js";
@@ -122,10 +123,12 @@ function AnySpendCustomInner({ loadOrder, mode = "modal", activeTab: activeTabPr
122
123
  const [selectedFiatPaymentMethod, setSelectedFiatPaymentMethod] = useState(FiatPaymentMethod.NONE);
123
124
  // Get current user's wallet
124
125
  const currentWallet = useAccountWallet();
125
- // Add state for custom recipient
126
- const [customRecipientAddress, setCustomRecipientAddress] = useState(recipientAddressProps);
127
- // Update recipient logic to use custom recipient
128
- const recipientAddress = customRecipientAddress || currentWallet.address;
126
+ // Recipient address state with dual-state system (auto + explicit user selection)
127
+ // The hook automatically manages priority: props > user selection > global address
128
+ const { setSelectedRecipientAddress, effectiveRecipientAddress: recipientAddress } = useRecipientAddressState({
129
+ recipientAddressFromProps: recipientAddressProps,
130
+ globalAddress: currentWallet.address,
131
+ });
129
132
  const [orderId, setOrderId] = useState(loadOrder);
130
133
  // Track if onSuccess has been called for the current order
131
134
  const onSuccessCalled = React.useRef(false);
@@ -519,8 +522,9 @@ function AnySpendCustomInner({ loadOrder, mode = "modal", activeTab: activeTabPr
519
522
  }
520
523
  }, className: "relative w-full", children: isCreatingOrder ? (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Loader2, { className: "size-4 animate-spin" }), _jsx("span", { children: "Creating order..." })] })) : isLoadingAnyspendQuote ? (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Loader2, { className: "size-4 animate-spin" }), _jsx("span", { children: "Loading quote..." })] })) : !recipientAddress ? ("Select recipient") : selectedFiatPaymentMethod === FiatPaymentMethod.NONE ? ("Select payment method") : anyspendQuote ? (_jsxs(_Fragment, { children: [_jsx("span", { children: "Buy" }), _jsx(ChevronRightCircle, { className: "absolute right-0 top-1/2 size-6 -translate-y-1/2 opacity-70" })] })) : ("No quote found") }) }) })] }) })] })] }));
521
524
  // Recipient selection view
522
- const recipientSelectionView = (_jsx("div", { className: cn("bg-as-surface-primary mx-auto w-[460px] max-w-full rounded-xl p-4"), children: _jsx(RecipientSelection, { initialValue: customRecipientAddress || "", title: "Add recipient address or ENS", description: "Send tokens to another address", onBack: () => setActivePanel(PanelView.CONFIRM_ORDER), onConfirm: address => {
523
- setCustomRecipientAddress(address);
525
+ const recipientSelectionView = (_jsx("div", { className: cn("bg-as-surface-primary mx-auto w-[460px] max-w-full rounded-xl p-4"), children: _jsx(RecipientSelection, { initialValue: recipientAddress || "", title: "Add recipient address or ENS", description: "Send tokens to another address", onBack: () => setActivePanel(PanelView.CONFIRM_ORDER), onConfirm: address => {
526
+ // User manually selected a recipient
527
+ setSelectedRecipientAddress(address);
524
528
  setActivePanel(PanelView.CONFIRM_ORDER);
525
529
  } }) }));
526
530
  // Crypto payment method view
@@ -168,7 +168,7 @@ function AnySpendCustomExactInInner({ loadOrder, mode = "modal", recipientAddres
168
168
  }
169
169
  };
170
170
  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." })] }) }));
171
- const mainView = (_jsxs("div", { className: "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 }) })), _jsx("div", { className: cn("relative -my-1 flex h-0 items-center justify-center", paymentType === "fiat" && "hidden"), children: _jsx(Button, { variant: "ghost", className: cn("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: true, selectedRecipientAddress: selectedRecipientOrDefault, recipientName: recipientName || undefined, onSelectRecipient: () => setActivePanel(PanelView.RECIPIENT_SELECTION), dstAmount: dstAmount, dstToken: selectedDstToken, dstTokenSymbol: DESTINATION_TOKEN_DETAILS.SYMBOL, dstTokenLogoURI: DESTINATION_TOKEN_DETAILS.LOGO_URI, selectedDstChainId: selectedDstChainId, setSelectedDstChainId: () => { }, setSelectedDstToken: () => { }, isSrcInputDirty: isSrcInputDirty, onChangeDstAmount: value => {
171
+ const mainView = (_jsxs("div", { className: "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 }) })), _jsx("div", { className: cn("relative -my-1 flex h-0 items-center justify-center", paymentType === "fiat" && "hidden"), children: _jsx(Button, { variant: "ghost", className: cn("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: true, effectiveRecipientAddress: selectedRecipientOrDefault, recipientName: recipientName || undefined, onSelectRecipient: () => setActivePanel(PanelView.RECIPIENT_SELECTION), dstAmount: dstAmount, dstToken: selectedDstToken, dstTokenSymbol: DESTINATION_TOKEN_DETAILS.SYMBOL, dstTokenLogoURI: DESTINATION_TOKEN_DETAILS.LOGO_URI, selectedDstChainId: selectedDstChainId, setSelectedDstChainId: () => { }, setSelectedDstToken: () => { }, isSrcInputDirty: isSrcInputDirty, onChangeDstAmount: value => {
172
172
  setIsSrcInputDirty(false);
173
173
  setSrcAmount(value);
174
174
  }, anyspendQuote: anyspendQuote, onShowPointsDetail: () => setActivePanel(PanelView.POINTS_DETAIL), onShowFeeDetail: () => 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: 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] }) }) }), mainFooter ? mainFooter : null] }));
@@ -4,7 +4,7 @@ import { useAccountWallet } from "../../../../global-account/react/index.js";
4
4
  import { cn } from "../../../../shared/utils/cn.js";
5
5
  import { shortenAddress } from "../../../../shared/utils/formatAddress.js";
6
6
  import { client } from "../../../../shared/utils/thirdweb.js";
7
- import { WalletCoinbase, WalletMetamask, WalletPhantom, WalletRainbow, WalletWalletConnect } from "@web3icons/react";
7
+ import { WalletCoinbase, WalletMetamask, WalletRainbow, WalletWalletConnect } from "@web3icons/react";
8
8
  import { ChevronLeft, ChevronRightCircle, Wallet, X, ZapIcon } from "lucide-react";
9
9
  import { useState } from "react";
10
10
  import { createPortal } from "react-dom";
@@ -94,13 +94,6 @@ export function CryptoPaymentMethod({ selectedPaymentMethod, setSelectedPaymentM
94
94
  description: "Connect using WalletConnect protocol",
95
95
  connector: availableConnectors.find(c => c.name === "WalletConnect"),
96
96
  },
97
- {
98
- id: "phantom",
99
- name: "Phantom",
100
- icon: _jsx(WalletPhantom, { size: 48 }),
101
- description: "Connect using Phantom wallet",
102
- connector: availableConnectors.find(c => c.name === "Phantom"),
103
- },
104
97
  ].filter(wallet => wallet.connector); // Only show wallets that have available connectors
105
98
  // Reset modal state when closing
106
99
  const handleCloseModal = () => {
@@ -1,14 +1,10 @@
1
1
  import { components } from "../../../types/api";
2
- import { CryptoPaymentMethodType } from "./CryptoPaymentMethod";
3
2
  interface CryptoReceiveSectionProps {
4
3
  isDepositMode?: boolean;
5
4
  isBuyMode?: boolean;
6
- selectedRecipientAddress?: string;
5
+ effectiveRecipientAddress?: string;
7
6
  recipientName?: string;
8
7
  onSelectRecipient: () => void;
9
- setRecipientAddress?: (address: string | undefined) => void;
10
- recipientAddressFromProps?: string;
11
- globalAddress?: string;
12
8
  dstAmount: string;
13
9
  dstToken: components["schemas"]["Token"];
14
10
  selectedDstChainId?: number;
@@ -21,7 +17,6 @@ interface CryptoReceiveSectionProps {
21
17
  dstTokenLogoURI?: string;
22
18
  onShowPointsDetail?: () => void;
23
19
  onShowFeeDetail?: () => void;
24
- selectedCryptoPaymentMethod?: CryptoPaymentMethodType;
25
20
  }
26
- export declare function CryptoReceiveSection({ isDepositMode, isBuyMode, selectedRecipientAddress, recipientName, onSelectRecipient, setRecipientAddress, recipientAddressFromProps, globalAddress, dstAmount, dstToken, selectedDstChainId, setSelectedDstChainId, setSelectedDstToken, isSrcInputDirty, onChangeDstAmount, anyspendQuote, dstTokenSymbol, dstTokenLogoURI, onShowPointsDetail, onShowFeeDetail, selectedCryptoPaymentMethod, }: CryptoReceiveSectionProps): import("react/jsx-runtime").JSX.Element;
21
+ export declare function CryptoReceiveSection({ isDepositMode, isBuyMode, effectiveRecipientAddress, recipientName, onSelectRecipient, dstAmount, dstToken, selectedDstChainId, setSelectedDstChainId, setSelectedDstToken, isSrcInputDirty, onChangeDstAmount, anyspendQuote, dstTokenSymbol, dstTokenLogoURI, onShowPointsDetail, onShowFeeDetail, }: CryptoReceiveSectionProps): import("react/jsx-runtime").JSX.Element;
27
22
  export {};
@@ -5,26 +5,16 @@ 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 { useEffect } from "react";
9
8
  import { useFeatureFlags } from "../../contexts/FeatureFlagsContext.js";
10
- import { useConnectedWalletDisplay } from "../../hooks/useConnectedWalletDisplay.js";
11
9
  import { OrderTokenAmount } from "./OrderTokenAmount.js";
12
10
  import { PointsBadge } from "./PointsBadge.js";
13
- export function CryptoReceiveSection({ isDepositMode = false, isBuyMode = false, selectedRecipientAddress, recipientName, onSelectRecipient, setRecipientAddress, recipientAddressFromProps, globalAddress, dstAmount, dstToken, selectedDstChainId, setSelectedDstChainId, setSelectedDstToken, isSrcInputDirty, onChangeDstAmount, anyspendQuote, dstTokenSymbol, dstTokenLogoURI, onShowPointsDetail, onShowFeeDetail, selectedCryptoPaymentMethod, }) {
11
+ export function CryptoReceiveSection({ isDepositMode = false, isBuyMode = false, effectiveRecipientAddress, recipientName, onSelectRecipient, dstAmount, dstToken, selectedDstChainId, setSelectedDstChainId, setSelectedDstToken, isSrcInputDirty, onChangeDstAmount, anyspendQuote, dstTokenSymbol, dstTokenLogoURI, onShowPointsDetail, onShowFeeDetail, }) {
14
12
  const featureFlags = useFeatureFlags();
15
- // Get wallet address based on selected payment method
16
- const { walletAddress } = useConnectedWalletDisplay(selectedCryptoPaymentMethod);
17
- // Set default recipient address when wallet changes
18
- useEffect(() => {
19
- if (setRecipientAddress) {
20
- setRecipientAddress(recipientAddressFromProps || walletAddress || globalAddress);
21
- }
22
- }, [recipientAddressFromProps, walletAddress, globalAddress, setRecipientAddress]);
23
- 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" }) }))] }), selectedRecipientAddress ? (_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(selectedRecipientAddress || "") }), _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 ? (
13
+ 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 ? (
24
14
  // Fixed destination token display for buy mode and deposit mode
25
15
  _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 })] })] })) : (
26
16
  // Token selection for regular swap mode
27
- _jsx(OrderTokenAmount, { address: selectedRecipientAddress, context: "to", inputValue: dstAmount, onChangeInput: onChangeDstAmount || (() => { }), chainId: selectedDstChainId || dstToken.chainId, setChainId: setSelectedDstChainId || (() => { }), token: dstToken, setToken: setSelectedDstToken || (() => { }) })), _jsxs("div", { className: "text-as-primary/50 flex h-5 items-center justify-start gap-2 text-sm", children: [_jsxs("div", { className: "flex items-center gap-2", children: [formatDisplayNumber(anyspendQuote?.data?.currencyOut?.amountUsd, {
17
+ _jsx(OrderTokenAmount, { address: effectiveRecipientAddress, context: "to", inputValue: dstAmount, onChangeInput: onChangeDstAmount || (() => { }), chainId: selectedDstChainId || dstToken.chainId, setChainId: setSelectedDstChainId || (() => { }), token: dstToken, setToken: setSelectedDstToken || (() => { }) })), _jsxs("div", { className: "text-as-primary/50 flex h-5 items-center justify-start gap-2 text-sm", children: [_jsxs("div", { className: "flex items-center gap-2", children: [formatDisplayNumber(anyspendQuote?.data?.currencyOut?.amountUsd, {
28
18
  style: "currency",
29
19
  fallback: "",
30
20
  }), anyspendQuote?.data?.currencyIn?.amountUsd &&
@@ -8,6 +8,7 @@ export * from "./useCoinbaseOnrampOptions";
8
8
  export * from "./useConnectedUserProfile";
9
9
  export * from "./useGeoOnrampOptions";
10
10
  export * from "./useGetGeo";
11
+ export * from "./useRecipientAddressState";
11
12
  export * from "./useSigMint";
12
13
  export * from "./useStripeClientSecret";
13
14
  export * from "./useStripeSupport";
@@ -8,6 +8,7 @@ export * from "./useCoinbaseOnrampOptions.js";
8
8
  export * from "./useConnectedUserProfile.js";
9
9
  export * from "./useGeoOnrampOptions.js";
10
10
  export * from "./useGetGeo.js";
11
+ export * from "./useRecipientAddressState.js";
11
12
  export * from "./useSigMint.js";
12
13
  export * from "./useStripeClientSecret.js";
13
14
  export * from "./useStripeSupport.js";
@@ -102,7 +102,7 @@ export declare function useAnyspendFlow({ paymentType, recipientAddress, loadOrd
102
102
  selectedFiatPaymentMethod: FiatPaymentMethod;
103
103
  setSelectedFiatPaymentMethod: import("react").Dispatch<import("react").SetStateAction<FiatPaymentMethod>>;
104
104
  selectedRecipientAddress: string | undefined;
105
- setSelectedRecipientAddress: import("react").Dispatch<import("react").SetStateAction<string | undefined>>;
105
+ setSelectedRecipientAddress: (address: string | undefined) => void;
106
106
  recipientName: string | null | undefined;
107
107
  globalAddress: string | undefined;
108
108
  hasEnoughBalance: boolean;
@@ -13,6 +13,7 @@ import { useAutoSelectCryptoPaymentMethod } from "./useAutoSelectCryptoPaymentMe
13
13
  import { useAutoSetActiveWalletFromWagmi } from "./useAutoSetActiveWalletFromWagmi.js";
14
14
  import { useConnectedWalletDisplay } from "./useConnectedWalletDisplay.js";
15
15
  import { useCryptoPaymentMethodState } from "./useCryptoPaymentMethodState.js";
16
+ import { useRecipientAddressState } from "./useRecipientAddressState.js";
16
17
  export var PanelView;
17
18
  (function (PanelView) {
18
19
  PanelView[PanelView["MAIN"] = 0] = "MAIN";
@@ -46,20 +47,19 @@ export function useAnyspendFlow({ paymentType = "crypto", recipientAddress, load
46
47
  // Payment method state with dual-state system (auto + explicit user selection)
47
48
  const { cryptoPaymentMethod, setCryptoPaymentMethod, selectedCryptoPaymentMethod, setSelectedCryptoPaymentMethod, effectiveCryptoPaymentMethod, resetPaymentMethods, } = useCryptoPaymentMethodState();
48
49
  const [selectedFiatPaymentMethod, setSelectedFiatPaymentMethod] = useState(FiatPaymentMethod.NONE);
49
- // Recipient state
50
+ // Recipient state with dual-state system (auto + explicit user selection)
50
51
  const { address: globalAddress } = useAccountWallet();
51
52
  const { walletAddress } = useConnectedWalletDisplay(effectiveCryptoPaymentMethod);
52
- const [selectedRecipientAddress, setSelectedRecipientAddress] = useState(recipientAddress);
53
- const recipientProfile = useProfile({ address: selectedRecipientAddress, fresh: true });
54
- const recipientName = recipientProfile.data?.name;
55
53
  // Auto-set active wallet from wagmi
56
54
  useAutoSetActiveWalletFromWagmi();
57
- // Set default recipient address when wallet changes
58
- useEffect(() => {
59
- if (!selectedRecipientAddress && globalAddress) {
60
- setSelectedRecipientAddress(globalAddress);
61
- }
62
- }, [selectedRecipientAddress, globalAddress]);
55
+ // Recipient address state - hook automatically manages priority: props > user selection > wallet/global
56
+ const { setSelectedRecipientAddress, effectiveRecipientAddress } = useRecipientAddressState({
57
+ recipientAddressFromProps: recipientAddress,
58
+ walletAddress,
59
+ globalAddress,
60
+ });
61
+ const recipientProfile = useProfile({ address: effectiveRecipientAddress, fresh: true });
62
+ const recipientName = recipientProfile.data?.name;
63
63
  // Check token balance for crypto payments
64
64
  const { rawBalance, isLoading: isBalanceLoading } = useTokenBalance({
65
65
  token: selectedSrcToken,
@@ -142,7 +142,7 @@ export function useAnyspendFlow({ paymentType = "crypto", recipientAddress, load
142
142
  dstTokenAddress: selectedDstToken.address,
143
143
  type: orderType,
144
144
  amount: activeInputAmountInWei,
145
- recipientAddress: selectedRecipientAddress,
145
+ recipientAddress: effectiveRecipientAddress,
146
146
  onrampVendor: paymentType === "fiat" ? getOnrampVendor(selectedFiatPaymentMethod) : undefined,
147
147
  });
148
148
  // Get geo options for fiat
@@ -257,7 +257,7 @@ export function useAnyspendFlow({ paymentType = "crypto", recipientAddress, load
257
257
  selectedFiatPaymentMethod,
258
258
  setSelectedFiatPaymentMethod,
259
259
  // Recipient
260
- selectedRecipientAddress,
260
+ selectedRecipientAddress: effectiveRecipientAddress,
261
261
  setSelectedRecipientAddress,
262
262
  recipientName,
263
263
  globalAddress,
@@ -8,6 +8,7 @@ import { CryptoPaymentMethodType } from "../components/common/CryptoPaymentMetho
8
8
  export function useConnectedWalletDisplay(selectedCryptoPaymentMethod) {
9
9
  const { connectedEOAWallet, connectedSmartWallet } = useAccountWallet();
10
10
  const { address: wagmiAddress, isConnected: wagmiWalletIsConnected } = useAccount();
11
+ const globalWalletAddress = connectedSmartWallet?.getAccount()?.address;
11
12
  // Helper function to check if two addresses are the same
12
13
  const isSameAddress = (addr1, addr2) => {
13
14
  if (!addr1 || !addr2)
@@ -16,7 +17,7 @@ export function useConnectedWalletDisplay(selectedCryptoPaymentMethod) {
16
17
  };
17
18
  // Check if connectedEOAWallet and wagmi wallet represent the same wallet
18
19
  const connectedEOAAddress = connectedEOAWallet?.getAccount()?.address;
19
- const isWalletDuplicated = isSameAddress(connectedEOAAddress, wagmiAddress);
20
+ const isWalletDuplicated = isSameAddress(connectedEOAAddress, wagmiAddress) || isSameAddress(globalWalletAddress, wagmiAddress);
20
21
  // Determine which wallet to show (prefer connectedEOAWallet if both exist and are the same)
21
22
  const shouldShowConnectedEOA = !!connectedEOAWallet;
22
23
  const shouldShowWagmiWallet = wagmiWalletIsConnected && (!isWalletDuplicated || !connectedEOAWallet);
@@ -0,0 +1,52 @@
1
+ interface UseRecipientAddressStateProps {
2
+ /** Fixed recipient address from props (highest priority) */
3
+ recipientAddressFromProps?: string;
4
+ /** Connected wallet address from payment method */
5
+ walletAddress?: string;
6
+ /** Global account address */
7
+ globalAddress?: string;
8
+ }
9
+ interface UseRecipientAddressStateResult {
10
+ /** User explicitly selected recipient address (undefined means no explicit selection) */
11
+ selectedRecipientAddress: string | undefined;
12
+ /** Function to update the user-selected recipient address */
13
+ setSelectedRecipientAddress: (address: string | undefined) => void;
14
+ /** Effective recipient address (follows priority: props > user selection > wallet/global) */
15
+ effectiveRecipientAddress: string | undefined;
16
+ /** Reset recipient address state */
17
+ resetRecipientAddress: () => void;
18
+ }
19
+ /**
20
+ * Custom hook to manage recipient address state with automatic priority handling:
21
+ *
22
+ * **Priority System:**
23
+ * 1. `recipientAddressFromProps` - Fixed recipient from component props (highest priority)
24
+ * 2. `selectedRecipientAddress` - User's explicit manual selection
25
+ * 3. `walletAddress` or `globalAddress` - Auto-selected fallback
26
+ *
27
+ * **Key Features:**
28
+ * - Automatically manages recipient address based on priority
29
+ * - Preserves user's manual selections
30
+ * - Updates automatically when wallet/global address changes (if no manual selection)
31
+ * - Derived value approach - no useEffect needed, no stale state bugs
32
+ *
33
+ * @example
34
+ * ```tsx
35
+ * const {
36
+ * selectedRecipientAddress,
37
+ * setSelectedRecipientAddress,
38
+ * effectiveRecipientAddress,
39
+ * resetRecipientAddress
40
+ * } = useRecipientAddressState({
41
+ * recipientAddressFromProps,
42
+ * walletAddress,
43
+ * globalAddress,
44
+ * });
45
+ *
46
+ * // Use effectiveRecipientAddress for display and operations
47
+ * // Use setSelectedRecipientAddress when user explicitly selects
48
+ * // Call resetRecipientAddress when switching tabs or going back
49
+ * ```
50
+ */
51
+ export declare function useRecipientAddressState({ recipientAddressFromProps, walletAddress, globalAddress, }?: UseRecipientAddressStateProps): UseRecipientAddressStateResult;
52
+ export {};
@@ -0,0 +1,49 @@
1
+ import { useState } from "react";
2
+ /**
3
+ * Custom hook to manage recipient address state with automatic priority handling:
4
+ *
5
+ * **Priority System:**
6
+ * 1. `recipientAddressFromProps` - Fixed recipient from component props (highest priority)
7
+ * 2. `selectedRecipientAddress` - User's explicit manual selection
8
+ * 3. `walletAddress` or `globalAddress` - Auto-selected fallback
9
+ *
10
+ * **Key Features:**
11
+ * - Automatically manages recipient address based on priority
12
+ * - Preserves user's manual selections
13
+ * - Updates automatically when wallet/global address changes (if no manual selection)
14
+ * - Derived value approach - no useEffect needed, no stale state bugs
15
+ *
16
+ * @example
17
+ * ```tsx
18
+ * const {
19
+ * selectedRecipientAddress,
20
+ * setSelectedRecipientAddress,
21
+ * effectiveRecipientAddress,
22
+ * resetRecipientAddress
23
+ * } = useRecipientAddressState({
24
+ * recipientAddressFromProps,
25
+ * walletAddress,
26
+ * globalAddress,
27
+ * });
28
+ *
29
+ * // Use effectiveRecipientAddress for display and operations
30
+ * // Use setSelectedRecipientAddress when user explicitly selects
31
+ * // Call resetRecipientAddress when switching tabs or going back
32
+ * ```
33
+ */
34
+ export function useRecipientAddressState({ recipientAddressFromProps, walletAddress, globalAddress, } = {}) {
35
+ // selectedRecipientAddress: explicitly selected by user (undefined means no explicit selection)
36
+ const [selectedRecipientAddress, setSelectedRecipientAddress] = useState(undefined);
37
+ // The effective recipient address, derived on each render, respecting priority.
38
+ const effectiveRecipientAddress = recipientAddressFromProps || selectedRecipientAddress || walletAddress || globalAddress;
39
+ // Helper function to reset user's manual selection.
40
+ const resetRecipientAddress = () => {
41
+ setSelectedRecipientAddress(undefined);
42
+ };
43
+ return {
44
+ selectedRecipientAddress,
45
+ setSelectedRecipientAddress,
46
+ effectiveRecipientAddress,
47
+ resetRecipientAddress,
48
+ };
49
+ }
@@ -1,14 +1,10 @@
1
1
  import { components } from "../../../types/api";
2
- import { CryptoPaymentMethodType } from "./CryptoPaymentMethod";
3
2
  interface CryptoReceiveSectionProps {
4
3
  isDepositMode?: boolean;
5
4
  isBuyMode?: boolean;
6
- selectedRecipientAddress?: string;
5
+ effectiveRecipientAddress?: string;
7
6
  recipientName?: string;
8
7
  onSelectRecipient: () => void;
9
- setRecipientAddress?: (address: string | undefined) => void;
10
- recipientAddressFromProps?: string;
11
- globalAddress?: string;
12
8
  dstAmount: string;
13
9
  dstToken: components["schemas"]["Token"];
14
10
  selectedDstChainId?: number;
@@ -21,7 +17,6 @@ interface CryptoReceiveSectionProps {
21
17
  dstTokenLogoURI?: string;
22
18
  onShowPointsDetail?: () => void;
23
19
  onShowFeeDetail?: () => void;
24
- selectedCryptoPaymentMethod?: CryptoPaymentMethodType;
25
20
  }
26
- export declare function CryptoReceiveSection({ isDepositMode, isBuyMode, selectedRecipientAddress, recipientName, onSelectRecipient, setRecipientAddress, recipientAddressFromProps, globalAddress, dstAmount, dstToken, selectedDstChainId, setSelectedDstChainId, setSelectedDstToken, isSrcInputDirty, onChangeDstAmount, anyspendQuote, dstTokenSymbol, dstTokenLogoURI, onShowPointsDetail, onShowFeeDetail, selectedCryptoPaymentMethod, }: CryptoReceiveSectionProps): import("react/jsx-runtime").JSX.Element;
21
+ export declare function CryptoReceiveSection({ isDepositMode, isBuyMode, effectiveRecipientAddress, recipientName, onSelectRecipient, dstAmount, dstToken, selectedDstChainId, setSelectedDstChainId, setSelectedDstToken, isSrcInputDirty, onChangeDstAmount, anyspendQuote, dstTokenSymbol, dstTokenLogoURI, onShowPointsDetail, onShowFeeDetail, }: CryptoReceiveSectionProps): import("react/jsx-runtime").JSX.Element;
27
22
  export {};
@@ -8,6 +8,7 @@ export * from "./useCoinbaseOnrampOptions";
8
8
  export * from "./useConnectedUserProfile";
9
9
  export * from "./useGeoOnrampOptions";
10
10
  export * from "./useGetGeo";
11
+ export * from "./useRecipientAddressState";
11
12
  export * from "./useSigMint";
12
13
  export * from "./useStripeClientSecret";
13
14
  export * from "./useStripeSupport";
@@ -102,7 +102,7 @@ export declare function useAnyspendFlow({ paymentType, recipientAddress, loadOrd
102
102
  selectedFiatPaymentMethod: FiatPaymentMethod;
103
103
  setSelectedFiatPaymentMethod: import("react").Dispatch<import("react").SetStateAction<FiatPaymentMethod>>;
104
104
  selectedRecipientAddress: string | undefined;
105
- setSelectedRecipientAddress: import("react").Dispatch<import("react").SetStateAction<string | undefined>>;
105
+ setSelectedRecipientAddress: (address: string | undefined) => void;
106
106
  recipientName: string | null | undefined;
107
107
  globalAddress: string | undefined;
108
108
  hasEnoughBalance: boolean;