@b3dotfun/sdk 0.0.67-alpha.0 → 0.0.67-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.
- package/dist/cjs/anyspend/react/components/AnySpend.js +38 -26
- package/dist/cjs/anyspend/react/components/AnySpendCustom.js +10 -6
- package/dist/cjs/anyspend/react/components/AnySpendCustomExactIn.js +1 -1
- package/dist/cjs/anyspend/react/components/common/CryptoPaymentMethod.js +0 -7
- package/dist/cjs/anyspend/react/components/common/CryptoReceiveSection.d.ts +2 -7
- package/dist/cjs/anyspend/react/components/common/CryptoReceiveSection.js +3 -13
- package/dist/cjs/anyspend/react/hooks/index.d.ts +1 -0
- package/dist/cjs/anyspend/react/hooks/index.js +1 -0
- package/dist/cjs/anyspend/react/hooks/useAnyspendFlow.d.ts +1 -1
- package/dist/cjs/anyspend/react/hooks/useAnyspendFlow.js +12 -12
- package/dist/cjs/anyspend/react/hooks/useConnectedWalletDisplay.js +2 -1
- package/dist/cjs/anyspend/react/hooks/useRecipientAddressState.d.ts +52 -0
- package/dist/cjs/anyspend/react/hooks/useRecipientAddressState.js +52 -0
- package/dist/esm/anyspend/react/components/AnySpend.js +38 -26
- package/dist/esm/anyspend/react/components/AnySpendCustom.js +10 -6
- package/dist/esm/anyspend/react/components/AnySpendCustomExactIn.js +1 -1
- package/dist/esm/anyspend/react/components/common/CryptoPaymentMethod.js +1 -8
- package/dist/esm/anyspend/react/components/common/CryptoReceiveSection.d.ts +2 -7
- package/dist/esm/anyspend/react/components/common/CryptoReceiveSection.js +3 -13
- package/dist/esm/anyspend/react/hooks/index.d.ts +1 -0
- package/dist/esm/anyspend/react/hooks/index.js +1 -0
- package/dist/esm/anyspend/react/hooks/useAnyspendFlow.d.ts +1 -1
- package/dist/esm/anyspend/react/hooks/useAnyspendFlow.js +12 -12
- package/dist/esm/anyspend/react/hooks/useConnectedWalletDisplay.js +2 -1
- package/dist/esm/anyspend/react/hooks/useRecipientAddressState.d.ts +52 -0
- package/dist/esm/anyspend/react/hooks/useRecipientAddressState.js +49 -0
- package/dist/types/anyspend/react/components/common/CryptoReceiveSection.d.ts +2 -7
- package/dist/types/anyspend/react/hooks/index.d.ts +1 -0
- package/dist/types/anyspend/react/hooks/useAnyspendFlow.d.ts +1 -1
- package/dist/types/anyspend/react/hooks/useRecipientAddressState.d.ts +52 -0
- package/package.json +1 -1
- package/src/anyspend/react/components/AnySpend.tsx +43 -31
- package/src/anyspend/react/components/AnySpendCustom.tsx +10 -7
- package/src/anyspend/react/components/AnySpendCustomExactIn.tsx +1 -1
- package/src/anyspend/react/components/common/CryptoPaymentMethod.tsx +1 -8
- package/src/anyspend/react/components/common/CryptoReceiveSection.tsx +5 -27
- package/src/anyspend/react/hooks/index.ts +1 -0
- package/src/anyspend/react/hooks/useAnyspendFlow.ts +13 -12
- package/src/anyspend/react/hooks/useConnectedWalletDisplay.ts +4 -1
- package/src/anyspend/react/hooks/useRecipientAddressState.ts +78 -0
|
@@ -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 {};
|
package/package.json
CHANGED
|
@@ -33,7 +33,9 @@ import { base, mainnet } from "viem/chains";
|
|
|
33
33
|
import { components } from "../../types/api";
|
|
34
34
|
import { useAutoSelectCryptoPaymentMethod } from "../hooks/useAutoSelectCryptoPaymentMethod";
|
|
35
35
|
import { useAutoSetActiveWalletFromWagmi } from "../hooks/useAutoSetActiveWalletFromWagmi";
|
|
36
|
+
import { useConnectedWalletDisplay } from "../hooks/useConnectedWalletDisplay";
|
|
36
37
|
import { useCryptoPaymentMethodState } from "../hooks/useCryptoPaymentMethodState";
|
|
38
|
+
import { useRecipientAddressState } from "../hooks/useRecipientAddressState";
|
|
37
39
|
import { AnySpendFingerprintWrapper, getFingerprintConfig } from "./AnySpendFingerprintWrapper";
|
|
38
40
|
import { CryptoPaymentMethod, CryptoPaymentMethodType } from "./common/CryptoPaymentMethod";
|
|
39
41
|
import { CryptoPaySection } from "./common/CryptoPaySection";
|
|
@@ -444,16 +446,29 @@ function AnySpendInner({
|
|
|
444
446
|
// [selectedDstChainId, newRecipientAddress, resolvedAddress]
|
|
445
447
|
// );
|
|
446
448
|
|
|
447
|
-
// State for recipient selection
|
|
448
|
-
const [recipientAddress, setRecipientAddress] = useState<string | undefined>();
|
|
449
|
-
|
|
450
449
|
const { address: globalAddress, wallet: globalWallet, connectedEOAWallet } = useAccountWallet();
|
|
451
|
-
const recipientProfile = useProfile({ address: recipientAddress, fresh: true });
|
|
452
|
-
const recipientName = recipientProfile.data?.name;
|
|
453
450
|
|
|
454
451
|
// Auto-set active wallet from wagmi
|
|
455
452
|
useAutoSetActiveWalletFromWagmi();
|
|
456
453
|
|
|
454
|
+
// Get wallet address based on selected payment method
|
|
455
|
+
const { walletAddress } = useConnectedWalletDisplay(effectiveCryptoPaymentMethod);
|
|
456
|
+
|
|
457
|
+
// Recipient address state with dual-state system (auto + explicit user selection)
|
|
458
|
+
// The hook automatically manages priority: props > user selection > wallet/global
|
|
459
|
+
const {
|
|
460
|
+
setSelectedRecipientAddress,
|
|
461
|
+
effectiveRecipientAddress,
|
|
462
|
+
// resetRecipientAddress, // Not used yet, but available for future use
|
|
463
|
+
} = useRecipientAddressState({
|
|
464
|
+
recipientAddressFromProps,
|
|
465
|
+
walletAddress,
|
|
466
|
+
globalAddress,
|
|
467
|
+
});
|
|
468
|
+
|
|
469
|
+
const recipientProfile = useProfile({ address: effectiveRecipientAddress, fresh: true });
|
|
470
|
+
const recipientName = recipientProfile.data?.name;
|
|
471
|
+
|
|
457
472
|
// Check token balance for crypto payments
|
|
458
473
|
const { rawBalance, isLoading: isBalanceLoading } = useTokenBalanceDirect({
|
|
459
474
|
token: selectedSrcToken,
|
|
@@ -515,7 +530,7 @@ function AnySpendInner({
|
|
|
515
530
|
type: "swap",
|
|
516
531
|
tradeType: isSrcInputDirty ? "EXACT_INPUT" : "EXACT_OUTPUT",
|
|
517
532
|
amount: activeInputAmountInWei,
|
|
518
|
-
recipientAddress,
|
|
533
|
+
recipientAddress: effectiveRecipientAddress,
|
|
519
534
|
}
|
|
520
535
|
: {
|
|
521
536
|
srcChain: base.id,
|
|
@@ -525,7 +540,7 @@ function AnySpendInner({
|
|
|
525
540
|
type: "swap",
|
|
526
541
|
tradeType: "EXACT_INPUT",
|
|
527
542
|
amount: srcAmountOnrampInWei,
|
|
528
|
-
recipientAddress,
|
|
543
|
+
recipientAddress: effectiveRecipientAddress,
|
|
529
544
|
onrampVendor: getOnrampVendor(selectedFiatPaymentMethod),
|
|
530
545
|
},
|
|
531
546
|
);
|
|
@@ -546,15 +561,15 @@ function AnySpendInner({
|
|
|
546
561
|
setCustomRecipients(parsedRecipients);
|
|
547
562
|
|
|
548
563
|
// If no wallet is connected and no recipient is selected, select the first recipient
|
|
549
|
-
if (!globalAddress && !
|
|
550
|
-
|
|
564
|
+
if (!globalAddress && !effectiveRecipientAddress && parsedRecipients.length > 0) {
|
|
565
|
+
setSelectedRecipientAddress(parsedRecipients[0].address);
|
|
551
566
|
}
|
|
552
567
|
}
|
|
553
568
|
} catch (err) {
|
|
554
569
|
console.error("Error loading recipients from local storage:", err);
|
|
555
570
|
}
|
|
556
571
|
// Only run this effect once on mount
|
|
557
|
-
}, [globalAddress,
|
|
572
|
+
}, [globalAddress, effectiveRecipientAddress, customRecipients.length, setSelectedRecipientAddress]);
|
|
558
573
|
|
|
559
574
|
// Update dependent amount when relay price changes
|
|
560
575
|
useEffect(() => {
|
|
@@ -666,7 +681,7 @@ function AnySpendInner({
|
|
|
666
681
|
|
|
667
682
|
if (activeTab === "fiat") {
|
|
668
683
|
// For fiat: check recipient first, then payment method
|
|
669
|
-
if (!
|
|
684
|
+
if (!effectiveRecipientAddress) return { text: "Select recipient", disable: false, error: false, loading: false };
|
|
670
685
|
|
|
671
686
|
// If no fiat payment method selected, show "Select payment method"
|
|
672
687
|
if (selectedFiatPaymentMethod === FiatPaymentMethod.NONE) {
|
|
@@ -685,7 +700,7 @@ function AnySpendInner({
|
|
|
685
700
|
}
|
|
686
701
|
|
|
687
702
|
// Check recipient after payment method
|
|
688
|
-
if (!
|
|
703
|
+
if (!effectiveRecipientAddress) return { text: "Select recipient", disable: false, error: false, loading: false };
|
|
689
704
|
|
|
690
705
|
// If payment method selected, show appropriate action
|
|
691
706
|
if (
|
|
@@ -704,7 +719,7 @@ function AnySpendInner({
|
|
|
704
719
|
activeInputAmountInWei,
|
|
705
720
|
isSameChainSameToken,
|
|
706
721
|
isLoadingAnyspendQuote,
|
|
707
|
-
|
|
722
|
+
effectiveRecipientAddress,
|
|
708
723
|
isCreatingOrder,
|
|
709
724
|
isCreatingOnrampOrder,
|
|
710
725
|
anyspendQuote,
|
|
@@ -722,12 +737,12 @@ function AnySpendInner({
|
|
|
722
737
|
|
|
723
738
|
if (activeTab === "fiat") {
|
|
724
739
|
// For fiat: check recipient first
|
|
725
|
-
if (!
|
|
740
|
+
if (!effectiveRecipientAddress) {
|
|
726
741
|
navigateToPanel(PanelView.RECIPIENT_SELECTION, "forward");
|
|
727
742
|
return;
|
|
728
743
|
}
|
|
729
744
|
|
|
730
|
-
invariant(
|
|
745
|
+
invariant(effectiveRecipientAddress, "Recipient address is not found");
|
|
731
746
|
|
|
732
747
|
// If no fiat payment method selected, show payment method selection
|
|
733
748
|
if (selectedFiatPaymentMethod === FiatPaymentMethod.NONE) {
|
|
@@ -750,12 +765,12 @@ function AnySpendInner({
|
|
|
750
765
|
}
|
|
751
766
|
|
|
752
767
|
// Check recipient after payment method
|
|
753
|
-
if (!
|
|
768
|
+
if (!effectiveRecipientAddress) {
|
|
754
769
|
navigateToPanel(PanelView.RECIPIENT_SELECTION, "forward");
|
|
755
770
|
return;
|
|
756
771
|
}
|
|
757
772
|
|
|
758
|
-
invariant(
|
|
773
|
+
invariant(effectiveRecipientAddress, "Recipient address is not found");
|
|
759
774
|
|
|
760
775
|
// If payment method is selected, create order with payment method info
|
|
761
776
|
if (
|
|
@@ -788,7 +803,7 @@ function AnySpendInner({
|
|
|
788
803
|
const handleCryptoSwap = async (method: CryptoPaymentMethodType) => {
|
|
789
804
|
try {
|
|
790
805
|
invariant(anyspendQuote, "Relay price is not found");
|
|
791
|
-
invariant(
|
|
806
|
+
invariant(effectiveRecipientAddress, "Recipient address is not found");
|
|
792
807
|
|
|
793
808
|
// Debug: Check payment method values
|
|
794
809
|
console.log("handleCryptoSwap - method parameter:", method);
|
|
@@ -797,7 +812,7 @@ function AnySpendInner({
|
|
|
797
812
|
const srcAmountBigInt = parseUnits(srcAmount.replace(/,/g, ""), selectedSrcToken.decimals);
|
|
798
813
|
|
|
799
814
|
createOrder({
|
|
800
|
-
recipientAddress,
|
|
815
|
+
recipientAddress: effectiveRecipientAddress,
|
|
801
816
|
orderType: "swap",
|
|
802
817
|
srcChain: selectedSrcChainId,
|
|
803
818
|
dstChain: isBuyMode ? destinationTokenChainId : selectedDstChainId,
|
|
@@ -819,7 +834,7 @@ function AnySpendInner({
|
|
|
819
834
|
const handleFiatOrder = async (paymentMethod: FiatPaymentMethod) => {
|
|
820
835
|
try {
|
|
821
836
|
invariant(anyspendQuote, "Relay price is not found");
|
|
822
|
-
invariant(
|
|
837
|
+
invariant(effectiveRecipientAddress, "Recipient address is not found");
|
|
823
838
|
|
|
824
839
|
if (!srcAmountOnRamp || parseFloat(srcAmountOnRamp) <= 0) {
|
|
825
840
|
toast.error("Please enter a valid amount");
|
|
@@ -862,7 +877,7 @@ function AnySpendInner({
|
|
|
862
877
|
};
|
|
863
878
|
|
|
864
879
|
createOnrampOrder({
|
|
865
|
-
recipientAddress,
|
|
880
|
+
recipientAddress: effectiveRecipientAddress,
|
|
866
881
|
orderType: "swap",
|
|
867
882
|
dstChain: getDstToken().chainId,
|
|
868
883
|
dstToken: getDstToken(),
|
|
@@ -1059,7 +1074,7 @@ function AnySpendInner({
|
|
|
1059
1074
|
setActivePanel(panelIndex);
|
|
1060
1075
|
}
|
|
1061
1076
|
}}
|
|
1062
|
-
_recipientAddress={
|
|
1077
|
+
_recipientAddress={effectiveRecipientAddress}
|
|
1063
1078
|
destinationToken={selectedDstToken}
|
|
1064
1079
|
destinationChainId={selectedDstChainId}
|
|
1065
1080
|
destinationAmount={dstAmount}
|
|
@@ -1118,12 +1133,9 @@ function AnySpendInner({
|
|
|
1118
1133
|
<CryptoReceiveSection
|
|
1119
1134
|
isDepositMode={false}
|
|
1120
1135
|
isBuyMode={isBuyMode}
|
|
1121
|
-
|
|
1136
|
+
effectiveRecipientAddress={effectiveRecipientAddress}
|
|
1122
1137
|
recipientName={recipientName || undefined}
|
|
1123
1138
|
onSelectRecipient={() => navigateToPanel(PanelView.RECIPIENT_SELECTION, "forward")}
|
|
1124
|
-
setRecipientAddress={setRecipientAddress}
|
|
1125
|
-
recipientAddressFromProps={recipientAddressFromProps}
|
|
1126
|
-
globalAddress={globalAddress}
|
|
1127
1139
|
dstAmount={dstAmount}
|
|
1128
1140
|
dstToken={selectedDstToken}
|
|
1129
1141
|
selectedDstChainId={selectedDstChainId}
|
|
@@ -1137,7 +1149,6 @@ function AnySpendInner({
|
|
|
1137
1149
|
anyspendQuote={anyspendQuote}
|
|
1138
1150
|
onShowPointsDetail={() => navigateToPanel(PanelView.POINTS_DETAIL, "forward")}
|
|
1139
1151
|
onShowFeeDetail={() => navigateToPanel(PanelView.FEE_DETAIL, "forward")}
|
|
1140
|
-
selectedCryptoPaymentMethod={effectiveCryptoPaymentMethod}
|
|
1141
1152
|
/>
|
|
1142
1153
|
)}
|
|
1143
1154
|
</div>
|
|
@@ -1165,7 +1176,7 @@ function AnySpendInner({
|
|
|
1165
1176
|
</div>
|
|
1166
1177
|
</ShinyButton>
|
|
1167
1178
|
|
|
1168
|
-
{!hideTransactionHistoryButton && (globalAddress ||
|
|
1179
|
+
{!hideTransactionHistoryButton && (globalAddress || effectiveRecipientAddress) ? (
|
|
1169
1180
|
<Button
|
|
1170
1181
|
variant="link"
|
|
1171
1182
|
onClick={onClickHistory}
|
|
@@ -1182,7 +1193,7 @@ function AnySpendInner({
|
|
|
1182
1193
|
<PanelOnrampPayment
|
|
1183
1194
|
srcAmountOnRamp={srcAmountOnRamp}
|
|
1184
1195
|
recipientName={recipientName || undefined}
|
|
1185
|
-
recipientAddress={
|
|
1196
|
+
recipientAddress={effectiveRecipientAddress}
|
|
1186
1197
|
isBuyMode={isBuyMode}
|
|
1187
1198
|
destinationTokenChainId={destinationTokenChainId}
|
|
1188
1199
|
destinationTokenAddress={destinationTokenAddress}
|
|
@@ -1213,10 +1224,11 @@ function AnySpendInner({
|
|
|
1213
1224
|
|
|
1214
1225
|
const recipientSelectionView = (
|
|
1215
1226
|
<RecipientSelection
|
|
1216
|
-
initialValue={
|
|
1227
|
+
initialValue={effectiveRecipientAddress || ""}
|
|
1217
1228
|
onBack={navigateBack}
|
|
1218
1229
|
onConfirm={address => {
|
|
1219
|
-
|
|
1230
|
+
// User manually selected a recipient
|
|
1231
|
+
setSelectedRecipientAddress(address);
|
|
1220
1232
|
navigateBack();
|
|
1221
1233
|
}}
|
|
1222
1234
|
/>
|
|
@@ -46,6 +46,7 @@ import { base } from "viem/chains";
|
|
|
46
46
|
import { useFeatureFlags } from "../contexts/FeatureFlagsContext";
|
|
47
47
|
import { useAutoSetActiveWalletFromWagmi } from "../hooks/useAutoSetActiveWalletFromWagmi";
|
|
48
48
|
import { useCryptoPaymentMethodState } from "../hooks/useCryptoPaymentMethodState";
|
|
49
|
+
import { useRecipientAddressState } from "../hooks/useRecipientAddressState";
|
|
49
50
|
import { AnySpendFingerprintWrapper, getFingerprintConfig } from "./AnySpendFingerprintWrapper";
|
|
50
51
|
import { CryptoPaymentMethod, CryptoPaymentMethodType } from "./common/CryptoPaymentMethod";
|
|
51
52
|
import { FeeBreakDown } from "./common/FeeBreakDown";
|
|
@@ -261,11 +262,12 @@ function AnySpendCustomInner({
|
|
|
261
262
|
// Get current user's wallet
|
|
262
263
|
const currentWallet = useAccountWallet();
|
|
263
264
|
|
|
264
|
-
//
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
265
|
+
// Recipient address state with dual-state system (auto + explicit user selection)
|
|
266
|
+
// The hook automatically manages priority: props > user selection > global address
|
|
267
|
+
const { setSelectedRecipientAddress, effectiveRecipientAddress: recipientAddress } = useRecipientAddressState({
|
|
268
|
+
recipientAddressFromProps: recipientAddressProps,
|
|
269
|
+
globalAddress: currentWallet.address,
|
|
270
|
+
});
|
|
269
271
|
|
|
270
272
|
const [orderId, setOrderId] = useState<string | undefined>(loadOrder);
|
|
271
273
|
|
|
@@ -1230,12 +1232,13 @@ function AnySpendCustomInner({
|
|
|
1230
1232
|
const recipientSelectionView = (
|
|
1231
1233
|
<div className={cn("bg-as-surface-primary mx-auto w-[460px] max-w-full rounded-xl p-4")}>
|
|
1232
1234
|
<RecipientSelection
|
|
1233
|
-
initialValue={
|
|
1235
|
+
initialValue={recipientAddress || ""}
|
|
1234
1236
|
title="Add recipient address or ENS"
|
|
1235
1237
|
description="Send tokens to another address"
|
|
1236
1238
|
onBack={() => setActivePanel(PanelView.CONFIRM_ORDER)}
|
|
1237
1239
|
onConfirm={address => {
|
|
1238
|
-
|
|
1240
|
+
// User manually selected a recipient
|
|
1241
|
+
setSelectedRecipientAddress(address);
|
|
1239
1242
|
setActivePanel(PanelView.CONFIRM_ORDER);
|
|
1240
1243
|
}}
|
|
1241
1244
|
/>
|
|
@@ -364,7 +364,7 @@ function AnySpendCustomExactInInner({
|
|
|
364
364
|
<CryptoReceiveSection
|
|
365
365
|
isDepositMode={false}
|
|
366
366
|
isBuyMode={true}
|
|
367
|
-
|
|
367
|
+
effectiveRecipientAddress={selectedRecipientOrDefault}
|
|
368
368
|
recipientName={recipientName || undefined}
|
|
369
369
|
onSelectRecipient={() => setActivePanel(PanelView.RECIPIENT_SELECTION)}
|
|
370
370
|
dstAmount={dstAmount}
|
|
@@ -4,7 +4,7 @@ import { useAccountWallet } from "@b3dotfun/sdk/global-account/react";
|
|
|
4
4
|
import { cn } from "@b3dotfun/sdk/shared/utils/cn";
|
|
5
5
|
import { shortenAddress } from "@b3dotfun/sdk/shared/utils/formatAddress";
|
|
6
6
|
import { client } from "@b3dotfun/sdk/shared/utils/thirdweb";
|
|
7
|
-
import { WalletCoinbase, WalletMetamask,
|
|
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";
|
|
@@ -127,13 +127,6 @@ export function CryptoPaymentMethod({
|
|
|
127
127
|
description: "Connect using WalletConnect protocol",
|
|
128
128
|
connector: availableConnectors.find(c => c.name === "WalletConnect"),
|
|
129
129
|
},
|
|
130
|
-
{
|
|
131
|
-
id: "phantom",
|
|
132
|
-
name: "Phantom",
|
|
133
|
-
icon: <WalletPhantom size={48} />,
|
|
134
|
-
description: "Connect using Phantom wallet",
|
|
135
|
-
connector: availableConnectors.find(c => c.name === "Phantom"),
|
|
136
|
-
},
|
|
137
130
|
].filter(wallet => wallet.connector); // Only show wallets that have available connectors
|
|
138
131
|
|
|
139
132
|
// Reset modal state when closing
|
|
@@ -4,11 +4,8 @@ import { shortenAddress } from "@b3dotfun/sdk/shared/utils/formatAddress";
|
|
|
4
4
|
import { formatDisplayNumber } from "@b3dotfun/sdk/shared/utils/number";
|
|
5
5
|
import { ChevronRight, Info } from "lucide-react";
|
|
6
6
|
import { motion } from "motion/react";
|
|
7
|
-
import { useEffect } from "react";
|
|
8
7
|
import { components } from "../../../types/api";
|
|
9
8
|
import { useFeatureFlags } from "../../contexts/FeatureFlagsContext";
|
|
10
|
-
import { useConnectedWalletDisplay } from "../../hooks/useConnectedWalletDisplay";
|
|
11
|
-
import { CryptoPaymentMethodType } from "./CryptoPaymentMethod";
|
|
12
9
|
import { OrderTokenAmount } from "./OrderTokenAmount";
|
|
13
10
|
import { PointsBadge } from "./PointsBadge";
|
|
14
11
|
|
|
@@ -16,12 +13,9 @@ interface CryptoReceiveSectionProps {
|
|
|
16
13
|
isDepositMode?: boolean;
|
|
17
14
|
isBuyMode?: boolean;
|
|
18
15
|
// Recipient data
|
|
19
|
-
|
|
16
|
+
effectiveRecipientAddress?: string;
|
|
20
17
|
recipientName?: string;
|
|
21
18
|
onSelectRecipient: () => void;
|
|
22
|
-
setRecipientAddress?: (address: string | undefined) => void;
|
|
23
|
-
recipientAddressFromProps?: string;
|
|
24
|
-
globalAddress?: string;
|
|
25
19
|
// Token data
|
|
26
20
|
dstAmount: string;
|
|
27
21
|
dstToken: components["schemas"]["Token"];
|
|
@@ -40,19 +34,14 @@ interface CryptoReceiveSectionProps {
|
|
|
40
34
|
onShowPointsDetail?: () => void;
|
|
41
35
|
// Fee detail navigation
|
|
42
36
|
onShowFeeDetail?: () => void;
|
|
43
|
-
// Payment method for wallet tracking
|
|
44
|
-
selectedCryptoPaymentMethod?: CryptoPaymentMethodType;
|
|
45
37
|
}
|
|
46
38
|
|
|
47
39
|
export function CryptoReceiveSection({
|
|
48
40
|
isDepositMode = false,
|
|
49
41
|
isBuyMode = false,
|
|
50
|
-
|
|
42
|
+
effectiveRecipientAddress,
|
|
51
43
|
recipientName,
|
|
52
44
|
onSelectRecipient,
|
|
53
|
-
setRecipientAddress,
|
|
54
|
-
recipientAddressFromProps,
|
|
55
|
-
globalAddress,
|
|
56
45
|
dstAmount,
|
|
57
46
|
dstToken,
|
|
58
47
|
selectedDstChainId,
|
|
@@ -65,20 +54,9 @@ export function CryptoReceiveSection({
|
|
|
65
54
|
dstTokenLogoURI,
|
|
66
55
|
onShowPointsDetail,
|
|
67
56
|
onShowFeeDetail,
|
|
68
|
-
selectedCryptoPaymentMethod,
|
|
69
57
|
}: CryptoReceiveSectionProps) {
|
|
70
58
|
const featureFlags = useFeatureFlags();
|
|
71
59
|
|
|
72
|
-
// Get wallet address based on selected payment method
|
|
73
|
-
const { walletAddress } = useConnectedWalletDisplay(selectedCryptoPaymentMethod);
|
|
74
|
-
|
|
75
|
-
// Set default recipient address when wallet changes
|
|
76
|
-
useEffect(() => {
|
|
77
|
-
if (setRecipientAddress) {
|
|
78
|
-
setRecipientAddress(recipientAddressFromProps || walletAddress || globalAddress);
|
|
79
|
-
}
|
|
80
|
-
}, [recipientAddressFromProps, walletAddress, globalAddress, setRecipientAddress]);
|
|
81
|
-
|
|
82
60
|
return (
|
|
83
61
|
<motion.div
|
|
84
62
|
initial={{ opacity: 0, y: 20, filter: "blur(10px)" }}
|
|
@@ -95,14 +73,14 @@ export function CryptoReceiveSection({
|
|
|
95
73
|
</button>
|
|
96
74
|
)}
|
|
97
75
|
</div>
|
|
98
|
-
{
|
|
76
|
+
{effectiveRecipientAddress ? (
|
|
99
77
|
<button
|
|
100
78
|
className={cn("text-as-tertiarry flex h-7 items-center gap-2 rounded-lg")}
|
|
101
79
|
onClick={onSelectRecipient}
|
|
102
80
|
>
|
|
103
81
|
<>
|
|
104
82
|
<span className="text-as-tertiarry flex items-center gap-1 text-sm">
|
|
105
|
-
{recipientName ? formatUsername(recipientName) : shortenAddress(
|
|
83
|
+
{recipientName ? formatUsername(recipientName) : shortenAddress(effectiveRecipientAddress || "")}
|
|
106
84
|
</span>
|
|
107
85
|
<ChevronRight className="h-4 w-4" />
|
|
108
86
|
</>
|
|
@@ -131,7 +109,7 @@ export function CryptoReceiveSection({
|
|
|
131
109
|
) : (
|
|
132
110
|
// Token selection for regular swap mode
|
|
133
111
|
<OrderTokenAmount
|
|
134
|
-
address={
|
|
112
|
+
address={effectiveRecipientAddress}
|
|
135
113
|
context="to"
|
|
136
114
|
inputValue={dstAmount}
|
|
137
115
|
onChangeInput={onChangeDstAmount || (() => {})}
|
|
@@ -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";
|
|
@@ -26,6 +26,7 @@ import { useAutoSelectCryptoPaymentMethod } from "./useAutoSelectCryptoPaymentMe
|
|
|
26
26
|
import { useAutoSetActiveWalletFromWagmi } from "./useAutoSetActiveWalletFromWagmi";
|
|
27
27
|
import { useConnectedWalletDisplay } from "./useConnectedWalletDisplay";
|
|
28
28
|
import { useCryptoPaymentMethodState } from "./useCryptoPaymentMethodState";
|
|
29
|
+
import { useRecipientAddressState } from "./useRecipientAddressState";
|
|
29
30
|
|
|
30
31
|
export enum PanelView {
|
|
31
32
|
MAIN,
|
|
@@ -105,22 +106,22 @@ export function useAnyspendFlow({
|
|
|
105
106
|
|
|
106
107
|
const [selectedFiatPaymentMethod, setSelectedFiatPaymentMethod] = useState<FiatPaymentMethod>(FiatPaymentMethod.NONE);
|
|
107
108
|
|
|
108
|
-
// Recipient state
|
|
109
|
+
// Recipient state with dual-state system (auto + explicit user selection)
|
|
109
110
|
const { address: globalAddress } = useAccountWallet();
|
|
110
111
|
const { walletAddress } = useConnectedWalletDisplay(effectiveCryptoPaymentMethod);
|
|
111
|
-
const [selectedRecipientAddress, setSelectedRecipientAddress] = useState<string | undefined>(recipientAddress);
|
|
112
|
-
const recipientProfile = useProfile({ address: selectedRecipientAddress, fresh: true });
|
|
113
|
-
const recipientName = recipientProfile.data?.name;
|
|
114
112
|
|
|
115
113
|
// Auto-set active wallet from wagmi
|
|
116
114
|
useAutoSetActiveWalletFromWagmi();
|
|
117
115
|
|
|
118
|
-
//
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
}
|
|
116
|
+
// Recipient address state - hook automatically manages priority: props > user selection > wallet/global
|
|
117
|
+
const { setSelectedRecipientAddress, effectiveRecipientAddress } = useRecipientAddressState({
|
|
118
|
+
recipientAddressFromProps: recipientAddress,
|
|
119
|
+
walletAddress,
|
|
120
|
+
globalAddress,
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
const recipientProfile = useProfile({ address: effectiveRecipientAddress, fresh: true });
|
|
124
|
+
const recipientName = recipientProfile.data?.name;
|
|
124
125
|
|
|
125
126
|
// Check token balance for crypto payments
|
|
126
127
|
const { rawBalance, isLoading: isBalanceLoading } = useTokenBalance({
|
|
@@ -208,7 +209,7 @@ export function useAnyspendFlow({
|
|
|
208
209
|
dstTokenAddress: selectedDstToken.address,
|
|
209
210
|
type: orderType,
|
|
210
211
|
amount: activeInputAmountInWei,
|
|
211
|
-
recipientAddress:
|
|
212
|
+
recipientAddress: effectiveRecipientAddress,
|
|
212
213
|
onrampVendor: paymentType === "fiat" ? getOnrampVendor(selectedFiatPaymentMethod) : undefined,
|
|
213
214
|
});
|
|
214
215
|
|
|
@@ -333,7 +334,7 @@ export function useAnyspendFlow({
|
|
|
333
334
|
selectedFiatPaymentMethod,
|
|
334
335
|
setSelectedFiatPaymentMethod,
|
|
335
336
|
// Recipient
|
|
336
|
-
selectedRecipientAddress,
|
|
337
|
+
selectedRecipientAddress: effectiveRecipientAddress,
|
|
337
338
|
setSelectedRecipientAddress,
|
|
338
339
|
recipientName,
|
|
339
340
|
globalAddress,
|
|
@@ -20,6 +20,8 @@ export function useConnectedWalletDisplay(
|
|
|
20
20
|
const { connectedEOAWallet, connectedSmartWallet } = useAccountWallet();
|
|
21
21
|
const { address: wagmiAddress, isConnected: wagmiWalletIsConnected } = useAccount();
|
|
22
22
|
|
|
23
|
+
const globalWalletAddress = connectedSmartWallet?.getAccount()?.address;
|
|
24
|
+
|
|
23
25
|
// Helper function to check if two addresses are the same
|
|
24
26
|
const isSameAddress = (addr1?: string, addr2?: string): boolean => {
|
|
25
27
|
if (!addr1 || !addr2) return false;
|
|
@@ -28,7 +30,8 @@ export function useConnectedWalletDisplay(
|
|
|
28
30
|
|
|
29
31
|
// Check if connectedEOAWallet and wagmi wallet represent the same wallet
|
|
30
32
|
const connectedEOAAddress = connectedEOAWallet?.getAccount()?.address;
|
|
31
|
-
const isWalletDuplicated =
|
|
33
|
+
const isWalletDuplicated =
|
|
34
|
+
isSameAddress(connectedEOAAddress, wagmiAddress) || isSameAddress(globalWalletAddress, wagmiAddress);
|
|
32
35
|
|
|
33
36
|
// Determine which wallet to show (prefer connectedEOAWallet if both exist and are the same)
|
|
34
37
|
const shouldShowConnectedEOA = !!connectedEOAWallet;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { useState } from "react";
|
|
2
|
+
|
|
3
|
+
interface UseRecipientAddressStateProps {
|
|
4
|
+
/** Fixed recipient address from props (highest priority) */
|
|
5
|
+
recipientAddressFromProps?: string;
|
|
6
|
+
/** Connected wallet address from payment method */
|
|
7
|
+
walletAddress?: string;
|
|
8
|
+
/** Global account address */
|
|
9
|
+
globalAddress?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
interface UseRecipientAddressStateResult {
|
|
13
|
+
/** User explicitly selected recipient address (undefined means no explicit selection) */
|
|
14
|
+
selectedRecipientAddress: string | undefined;
|
|
15
|
+
/** Function to update the user-selected recipient address */
|
|
16
|
+
setSelectedRecipientAddress: (address: string | undefined) => void;
|
|
17
|
+
/** Effective recipient address (follows priority: props > user selection > wallet/global) */
|
|
18
|
+
effectiveRecipientAddress: string | undefined;
|
|
19
|
+
/** Reset recipient address state */
|
|
20
|
+
resetRecipientAddress: () => void;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Custom hook to manage recipient address state with automatic priority handling:
|
|
25
|
+
*
|
|
26
|
+
* **Priority System:**
|
|
27
|
+
* 1. `recipientAddressFromProps` - Fixed recipient from component props (highest priority)
|
|
28
|
+
* 2. `selectedRecipientAddress` - User's explicit manual selection
|
|
29
|
+
* 3. `walletAddress` or `globalAddress` - Auto-selected fallback
|
|
30
|
+
*
|
|
31
|
+
* **Key Features:**
|
|
32
|
+
* - Automatically manages recipient address based on priority
|
|
33
|
+
* - Preserves user's manual selections
|
|
34
|
+
* - Updates automatically when wallet/global address changes (if no manual selection)
|
|
35
|
+
* - Derived value approach - no useEffect needed, no stale state bugs
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* ```tsx
|
|
39
|
+
* const {
|
|
40
|
+
* selectedRecipientAddress,
|
|
41
|
+
* setSelectedRecipientAddress,
|
|
42
|
+
* effectiveRecipientAddress,
|
|
43
|
+
* resetRecipientAddress
|
|
44
|
+
* } = useRecipientAddressState({
|
|
45
|
+
* recipientAddressFromProps,
|
|
46
|
+
* walletAddress,
|
|
47
|
+
* globalAddress,
|
|
48
|
+
* });
|
|
49
|
+
*
|
|
50
|
+
* // Use effectiveRecipientAddress for display and operations
|
|
51
|
+
* // Use setSelectedRecipientAddress when user explicitly selects
|
|
52
|
+
* // Call resetRecipientAddress when switching tabs or going back
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
export function useRecipientAddressState({
|
|
56
|
+
recipientAddressFromProps,
|
|
57
|
+
walletAddress,
|
|
58
|
+
globalAddress,
|
|
59
|
+
}: UseRecipientAddressStateProps = {}): UseRecipientAddressStateResult {
|
|
60
|
+
// selectedRecipientAddress: explicitly selected by user (undefined means no explicit selection)
|
|
61
|
+
const [selectedRecipientAddress, setSelectedRecipientAddress] = useState<string | undefined>(undefined);
|
|
62
|
+
|
|
63
|
+
// The effective recipient address, derived on each render, respecting priority.
|
|
64
|
+
const effectiveRecipientAddress =
|
|
65
|
+
recipientAddressFromProps || selectedRecipientAddress || walletAddress || globalAddress;
|
|
66
|
+
|
|
67
|
+
// Helper function to reset user's manual selection.
|
|
68
|
+
const resetRecipientAddress = () => {
|
|
69
|
+
setSelectedRecipientAddress(undefined);
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
return {
|
|
73
|
+
selectedRecipientAddress,
|
|
74
|
+
setSelectedRecipientAddress,
|
|
75
|
+
effectiveRecipientAddress,
|
|
76
|
+
resetRecipientAddress,
|
|
77
|
+
};
|
|
78
|
+
}
|