@b3dotfun/sdk 0.0.62 → 0.0.63-test.0-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 (150) hide show
  1. package/dist/cjs/anyspend/react/components/AnySpend.js +61 -23
  2. package/dist/cjs/anyspend/react/components/AnySpendCustom.js +3 -0
  3. package/dist/cjs/anyspend/react/components/AnySpendCustomExactIn.d.ts +34 -0
  4. package/dist/cjs/anyspend/react/components/AnySpendCustomExactIn.js +275 -0
  5. package/dist/cjs/anyspend/react/components/AnySpendStakeB3.js +5 -4
  6. package/dist/cjs/anyspend/react/components/AnySpendStakeB3ExactIn.d.ts +9 -0
  7. package/dist/cjs/anyspend/react/components/AnySpendStakeB3ExactIn.js +288 -0
  8. package/dist/cjs/anyspend/react/components/AnySpendStakeUpsideExactIn.d.ts +11 -0
  9. package/dist/cjs/anyspend/react/components/AnySpendStakeUpsideExactIn.js +33 -0
  10. package/dist/cjs/anyspend/react/components/AnyspendDepositHype.js +4 -4
  11. package/dist/cjs/anyspend/react/components/common/CryptoPaySection.js +4 -6
  12. package/dist/cjs/anyspend/react/components/common/CryptoPaymentMethod.js +9 -17
  13. package/dist/cjs/anyspend/react/components/common/CryptoReceiveSection.d.ts +6 -1
  14. package/dist/cjs/anyspend/react/components/common/CryptoReceiveSection.js +11 -1
  15. package/dist/cjs/anyspend/react/components/common/OrderDetails.js +66 -147
  16. package/dist/cjs/anyspend/react/components/common/OrderDetailsCollapsible.js +2 -3
  17. package/dist/cjs/anyspend/react/components/common/OrderTokenAmount.d.ts +2 -1
  18. package/dist/cjs/anyspend/react/components/common/OrderTokenAmount.js +39 -15
  19. package/dist/cjs/anyspend/react/components/common/PaySection.js +1 -1
  20. package/dist/cjs/anyspend/react/components/common/TokenBalance.js +1 -1
  21. package/dist/cjs/anyspend/react/components/index.d.ts +5 -1
  22. package/dist/cjs/anyspend/react/components/index.js +11 -3
  23. package/dist/cjs/anyspend/react/hooks/useAnyspendFlow.d.ts +25 -3
  24. package/dist/cjs/anyspend/react/hooks/useAnyspendFlow.js +42 -19
  25. package/dist/cjs/anyspend/react/hooks/useAnyspendOrderHistory.d.ts +116 -0
  26. package/dist/cjs/anyspend/react/hooks/useAnyspendQuote.js +1 -1
  27. package/dist/cjs/anyspend/react/hooks/useAutoSelectCryptoPaymentMethod.d.ts +26 -0
  28. package/dist/cjs/anyspend/react/hooks/useAutoSelectCryptoPaymentMethod.js +56 -0
  29. package/dist/cjs/anyspend/react/hooks/useAutoSetActiveWalletFromWagmi.d.ts +10 -0
  30. package/dist/cjs/anyspend/react/hooks/useAutoSetActiveWalletFromWagmi.js +73 -0
  31. package/dist/cjs/anyspend/react/hooks/useConnectedWalletDisplay.d.ts +14 -0
  32. package/dist/cjs/anyspend/react/hooks/useConnectedWalletDisplay.js +57 -0
  33. package/dist/cjs/anyspend/react/hooks/usePhantomTransfer.d.ts +36 -0
  34. package/dist/cjs/anyspend/react/hooks/usePhantomTransfer.js +211 -0
  35. package/dist/cjs/anyspend/types/api.d.ts +665 -3
  36. package/dist/cjs/anyspend/utils/orderPayload.js +4 -0
  37. package/dist/cjs/global-account/react/components/B3DynamicModal.js +10 -1
  38. package/dist/cjs/global-account/react/components/SignInWithB3/SignInWithB3Flow.js +3 -1
  39. package/dist/cjs/global-account/react/components/SignInWithB3/steps/LoginStep.js +2 -2
  40. package/dist/cjs/global-account/react/hooks/index.d.ts +2 -1
  41. package/dist/cjs/global-account/react/hooks/index.js +5 -3
  42. package/dist/cjs/global-account/react/hooks/useAuthentication.d.ts +2 -2
  43. package/dist/cjs/global-account/react/hooks/useAuthentication.js +7 -2
  44. package/dist/cjs/global-account/react/hooks/useSimBalance.d.ts +1 -1
  45. package/dist/cjs/global-account/react/hooks/useSimBalance.js +6 -5
  46. package/dist/cjs/global-account/react/hooks/useTokenBalanceDirect.d.ts +12 -0
  47. package/dist/cjs/global-account/react/hooks/useTokenBalanceDirect.js +62 -0
  48. package/dist/cjs/global-account/react/hooks/useTokenFromUrl.js +4 -3
  49. package/dist/cjs/global-account/react/stores/useModalStore.d.ts +31 -1
  50. package/dist/esm/anyspend/react/components/AnySpend.js +62 -24
  51. package/dist/esm/anyspend/react/components/AnySpendCustom.js +3 -0
  52. package/dist/esm/anyspend/react/components/AnySpendCustomExactIn.d.ts +34 -0
  53. package/dist/esm/anyspend/react/components/AnySpendCustomExactIn.js +269 -0
  54. package/dist/esm/anyspend/react/components/AnySpendStakeB3.js +7 -6
  55. package/dist/esm/anyspend/react/components/AnySpendStakeB3ExactIn.d.ts +9 -0
  56. package/dist/esm/anyspend/react/components/AnySpendStakeB3ExactIn.js +285 -0
  57. package/dist/esm/anyspend/react/components/AnySpendStakeUpsideExactIn.d.ts +11 -0
  58. package/dist/esm/anyspend/react/components/AnySpendStakeUpsideExactIn.js +30 -0
  59. package/dist/esm/anyspend/react/components/AnyspendDepositHype.js +4 -4
  60. package/dist/esm/anyspend/react/components/common/CryptoPaySection.js +5 -7
  61. package/dist/esm/anyspend/react/components/common/CryptoPaymentMethod.js +9 -17
  62. package/dist/esm/anyspend/react/components/common/CryptoReceiveSection.d.ts +6 -1
  63. package/dist/esm/anyspend/react/components/common/CryptoReceiveSection.js +11 -1
  64. package/dist/esm/anyspend/react/components/common/OrderDetails.js +67 -148
  65. package/dist/esm/anyspend/react/components/common/OrderDetailsCollapsible.js +2 -3
  66. package/dist/esm/anyspend/react/components/common/OrderTokenAmount.d.ts +2 -1
  67. package/dist/esm/anyspend/react/components/common/OrderTokenAmount.js +40 -16
  68. package/dist/esm/anyspend/react/components/common/PaySection.js +1 -1
  69. package/dist/esm/anyspend/react/components/common/TokenBalance.js +2 -2
  70. package/dist/esm/anyspend/react/components/index.d.ts +5 -1
  71. package/dist/esm/anyspend/react/components/index.js +5 -1
  72. package/dist/esm/anyspend/react/hooks/useAnyspendFlow.d.ts +25 -3
  73. package/dist/esm/anyspend/react/hooks/useAnyspendFlow.js +42 -19
  74. package/dist/esm/anyspend/react/hooks/useAnyspendOrderHistory.d.ts +116 -0
  75. package/dist/esm/anyspend/react/hooks/useAnyspendQuote.js +1 -1
  76. package/dist/esm/anyspend/react/hooks/useAutoSelectCryptoPaymentMethod.d.ts +26 -0
  77. package/dist/esm/anyspend/react/hooks/useAutoSelectCryptoPaymentMethod.js +53 -0
  78. package/dist/esm/anyspend/react/hooks/useAutoSetActiveWalletFromWagmi.d.ts +10 -0
  79. package/dist/esm/anyspend/react/hooks/useAutoSetActiveWalletFromWagmi.js +70 -0
  80. package/dist/esm/anyspend/react/hooks/useConnectedWalletDisplay.d.ts +14 -0
  81. package/dist/esm/anyspend/react/hooks/useConnectedWalletDisplay.js +54 -0
  82. package/dist/esm/anyspend/react/hooks/usePhantomTransfer.d.ts +36 -0
  83. package/dist/esm/anyspend/react/hooks/usePhantomTransfer.js +208 -0
  84. package/dist/esm/anyspend/types/api.d.ts +665 -3
  85. package/dist/esm/anyspend/utils/orderPayload.js +4 -0
  86. package/dist/esm/global-account/react/components/B3DynamicModal.js +11 -2
  87. package/dist/esm/global-account/react/components/SignInWithB3/SignInWithB3Flow.js +3 -1
  88. package/dist/esm/global-account/react/components/SignInWithB3/steps/LoginStep.js +2 -2
  89. package/dist/esm/global-account/react/hooks/index.d.ts +2 -1
  90. package/dist/esm/global-account/react/hooks/index.js +2 -1
  91. package/dist/esm/global-account/react/hooks/useAuthentication.d.ts +2 -2
  92. package/dist/esm/global-account/react/hooks/useAuthentication.js +7 -2
  93. package/dist/esm/global-account/react/hooks/useSimBalance.d.ts +1 -1
  94. package/dist/esm/global-account/react/hooks/useSimBalance.js +6 -5
  95. package/dist/esm/global-account/react/hooks/useTokenBalanceDirect.d.ts +12 -0
  96. package/dist/esm/global-account/react/hooks/useTokenBalanceDirect.js +59 -0
  97. package/dist/esm/global-account/react/hooks/useTokenFromUrl.js +4 -3
  98. package/dist/esm/global-account/react/stores/useModalStore.d.ts +31 -1
  99. package/dist/types/anyspend/react/components/AnySpendCustomExactIn.d.ts +34 -0
  100. package/dist/types/anyspend/react/components/AnySpendStakeB3ExactIn.d.ts +9 -0
  101. package/dist/types/anyspend/react/components/AnySpendStakeUpsideExactIn.d.ts +11 -0
  102. package/dist/types/anyspend/react/components/common/CryptoReceiveSection.d.ts +6 -1
  103. package/dist/types/anyspend/react/components/common/OrderTokenAmount.d.ts +2 -1
  104. package/dist/types/anyspend/react/components/index.d.ts +5 -1
  105. package/dist/types/anyspend/react/hooks/useAnyspendFlow.d.ts +25 -3
  106. package/dist/types/anyspend/react/hooks/useAnyspendOrderHistory.d.ts +116 -0
  107. package/dist/types/anyspend/react/hooks/useAutoSelectCryptoPaymentMethod.d.ts +26 -0
  108. package/dist/types/anyspend/react/hooks/useAutoSetActiveWalletFromWagmi.d.ts +10 -0
  109. package/dist/types/anyspend/react/hooks/useConnectedWalletDisplay.d.ts +14 -0
  110. package/dist/types/anyspend/react/hooks/usePhantomTransfer.d.ts +36 -0
  111. package/dist/types/anyspend/types/api.d.ts +665 -3
  112. package/dist/types/global-account/react/hooks/index.d.ts +2 -1
  113. package/dist/types/global-account/react/hooks/useAuthentication.d.ts +2 -2
  114. package/dist/types/global-account/react/hooks/useSimBalance.d.ts +1 -1
  115. package/dist/types/global-account/react/hooks/useTokenBalanceDirect.d.ts +12 -0
  116. package/dist/types/global-account/react/stores/useModalStore.d.ts +31 -1
  117. package/package.json +3 -2
  118. package/src/anyspend/react/components/AnySpend.tsx +73 -22
  119. package/src/anyspend/react/components/AnySpendCustom.tsx +4 -0
  120. package/src/anyspend/react/components/AnySpendCustomExactIn.tsx +595 -0
  121. package/src/anyspend/react/components/AnySpendStakeB3.tsx +8 -11
  122. package/src/anyspend/react/components/AnySpendStakeB3ExactIn.tsx +522 -0
  123. package/src/anyspend/react/components/AnySpendStakeUpsideExactIn.tsx +73 -0
  124. package/src/anyspend/react/components/AnyspendDepositHype.tsx +7 -3
  125. package/src/anyspend/react/components/common/CryptoPaySection.tsx +5 -7
  126. package/src/anyspend/react/components/common/CryptoPaymentMethod.tsx +9 -18
  127. package/src/anyspend/react/components/common/CryptoReceiveSection.tsx +22 -0
  128. package/src/anyspend/react/components/common/OrderDetails.tsx +76 -190
  129. package/src/anyspend/react/components/common/OrderDetailsCollapsible.tsx +2 -3
  130. package/src/anyspend/react/components/common/OrderTokenAmount.tsx +48 -17
  131. package/src/anyspend/react/components/common/PaySection.tsx +1 -0
  132. package/src/anyspend/react/components/common/TokenBalance.tsx +2 -2
  133. package/src/anyspend/react/components/index.ts +5 -1
  134. package/src/anyspend/react/hooks/useAnyspendFlow.ts +51 -18
  135. package/src/anyspend/react/hooks/useAnyspendQuote.ts +1 -1
  136. package/src/anyspend/react/hooks/useAutoSelectCryptoPaymentMethod.ts +72 -0
  137. package/src/anyspend/react/hooks/useAutoSetActiveWalletFromWagmi.ts +80 -0
  138. package/src/anyspend/react/hooks/useConnectedWalletDisplay.ts +69 -0
  139. package/src/anyspend/react/hooks/usePhantomTransfer.ts +301 -0
  140. package/src/anyspend/types/api.ts +669 -1
  141. package/src/anyspend/utils/orderPayload.ts +5 -1
  142. package/src/global-account/react/components/B3DynamicModal.tsx +11 -1
  143. package/src/global-account/react/components/SignInWithB3/SignInWithB3Flow.tsx +3 -1
  144. package/src/global-account/react/components/SignInWithB3/steps/LoginStep.tsx +2 -2
  145. package/src/global-account/react/hooks/index.ts +2 -1
  146. package/src/global-account/react/hooks/useAuthentication.ts +10 -2
  147. package/src/global-account/react/hooks/useSimBalance.ts +6 -5
  148. package/src/global-account/react/hooks/useTokenBalanceDirect.tsx +84 -0
  149. package/src/global-account/react/hooks/useTokenFromUrl.tsx +6 -5
  150. package/src/global-account/react/stores/useModalStore.ts +34 -0
@@ -23,6 +23,8 @@ import { useAccount } from "wagmi";
23
23
  import { components } from "../../types/api";
24
24
  import { CryptoPaymentMethodType } from "../components/common/CryptoPaymentMethod";
25
25
  import { FiatPaymentMethod } from "../components/common/FiatPaymentMethod";
26
+ import { useAutoSelectCryptoPaymentMethod } from "./useAutoSelectCryptoPaymentMethod";
27
+ import { useAutoSetActiveWalletFromWagmi } from "./useAutoSetActiveWalletFromWagmi";
26
28
 
27
29
  export enum PanelView {
28
30
  MAIN,
@@ -41,13 +43,17 @@ interface UseAnyspendFlowProps {
41
43
  loadOrder?: string;
42
44
  isDepositMode?: boolean;
43
45
  onOrderSuccess?: (orderId: string) => void;
44
- onTransactionSuccess?: (amount?: string) => void;
46
+ onTransactionSuccess?: (amount: string) => void;
45
47
  sourceTokenAddress?: string;
46
48
  sourceTokenChainId?: number;
49
+ destinationTokenAddress?: string;
50
+ destinationTokenChainId?: number;
47
51
  slippage?: number;
48
52
  disableUrlParamManagement?: boolean;
53
+ orderType?: "hype_duel" | "custom_exact_in";
49
54
  }
50
55
 
56
+ // This hook serves for order hype_duel and custom_exact_in
51
57
  export function useAnyspendFlow({
52
58
  paymentType = "crypto",
53
59
  recipientAddress,
@@ -57,8 +63,11 @@ export function useAnyspendFlow({
57
63
  onTransactionSuccess,
58
64
  sourceTokenAddress,
59
65
  sourceTokenChainId,
66
+ destinationTokenAddress,
67
+ destinationTokenChainId,
60
68
  slippage = 0,
61
69
  disableUrlParamManagement = false,
70
+ orderType = "hype_duel",
62
71
  }: UseAnyspendFlowProps) {
63
72
  const searchParams = useSearchParamsSSR();
64
73
  const router = useRouter();
@@ -68,17 +77,21 @@ export function useAnyspendFlow({
68
77
  const [orderId, setOrderId] = useState<string | undefined>(loadOrder);
69
78
  const { orderAndTransactions: oat } = useAnyspendOrderAndTransactions(orderId);
70
79
 
71
- // Token selection state - use provided sourceTokenChainId if available
80
+ // Token selection state - use provided sourceTokenChainId and destinationTokenChainId if available
72
81
  const [selectedSrcChainId, setSelectedSrcChainId] = useState<number>(
73
82
  sourceTokenChainId || (paymentType === "fiat" ? base.id : mainnet.id),
74
83
  );
75
- const [selectedDstChainId, setSelectedDstChainId] = useState<number>(base.id); // Default to Base for cross-chain swaps
76
84
  const defaultSrcToken = paymentType === "fiat" ? USDC_BASE : getDefaultToken(selectedSrcChainId);
85
+ const defaultDstToken = B3_TOKEN; // Default destination token
77
86
  const [selectedSrcToken, setSelectedSrcToken] = useState<components["schemas"]["Token"]>(defaultSrcToken);
87
+ const [selectedDstToken, setSelectedDstToken] = useState<components["schemas"]["Token"]>(defaultDstToken);
78
88
  const [srcAmount, setSrcAmount] = useState<string>(paymentType === "fiat" ? "5" : "0.1");
79
89
  const [dstAmount, setDstAmount] = useState<string>("");
80
90
  const [isSrcInputDirty, setIsSrcInputDirty] = useState(true);
81
91
 
92
+ // Derive destination chain ID from token or prop (cannot change)
93
+ const selectedDstChainId = destinationTokenChainId || selectedDstToken.chainId;
94
+
82
95
  // Payment method state
83
96
  const [selectedCryptoPaymentMethod, setSelectedCryptoPaymentMethod] = useState<CryptoPaymentMethodType>(
84
97
  CryptoPaymentMethodType.NONE,
@@ -92,6 +105,9 @@ export function useAnyspendFlow({
92
105
  const recipientProfile = useProfile({ address: selectedRecipientAddress, fresh: true });
93
106
  const recipientName = recipientProfile.data?.name;
94
107
 
108
+ // Auto-set active wallet from wagmi
109
+ useAutoSetActiveWalletFromWagmi();
110
+
95
111
  // Set default recipient address when wallet changes
96
112
  useEffect(() => {
97
113
  if (!selectedRecipientAddress && globalAddress) {
@@ -116,16 +132,14 @@ export function useAnyspendFlow({
116
132
  }
117
133
  }, [rawBalance, srcAmount, selectedSrcToken.decimals, isBalanceLoading, paymentType]);
118
134
 
119
- // Auto-set crypto payment method based on balance
120
- useEffect(() => {
121
- if (paymentType === "crypto" && !isBalanceLoading) {
122
- if (hasEnoughBalance) {
123
- setSelectedCryptoPaymentMethod(CryptoPaymentMethodType.CONNECT_WALLET);
124
- } else {
125
- setSelectedCryptoPaymentMethod(CryptoPaymentMethodType.TRANSFER_CRYPTO);
126
- }
127
- }
128
- }, [paymentType, hasEnoughBalance, isBalanceLoading]);
135
+ // Auto-select crypto payment method based on available wallets and balance
136
+ useAutoSelectCryptoPaymentMethod({
137
+ paymentType,
138
+ selectedCryptoPaymentMethod,
139
+ setSelectedCryptoPaymentMethod,
140
+ hasEnoughBalance,
141
+ isBalanceLoading,
142
+ });
129
143
 
130
144
  // Fetch specific token when sourceTokenAddress and sourceTokenChainId are provided
131
145
  useEffect(() => {
@@ -145,6 +159,24 @@ export function useAnyspendFlow({
145
159
  fetchSourceToken();
146
160
  }, [sourceTokenAddress, sourceTokenChainId]);
147
161
 
162
+ // Fetch specific token when destinationTokenAddress and destinationTokenChainId are provided
163
+ useEffect(() => {
164
+ const fetchDestinationToken = async () => {
165
+ if (destinationTokenAddress && destinationTokenChainId) {
166
+ try {
167
+ const token = await anyspendService.getToken(destinationTokenChainId, destinationTokenAddress);
168
+ setSelectedDstToken(token);
169
+ } catch (error) {
170
+ console.error("Failed to fetch destination token:", error);
171
+ toast.error(`Failed to load token ${destinationTokenAddress} on chain ${destinationTokenChainId}`);
172
+ // Keep the default token on error
173
+ }
174
+ }
175
+ };
176
+
177
+ fetchDestinationToken();
178
+ }, [destinationTokenAddress, destinationTokenChainId]);
179
+
148
180
  // Helper function for onramp vendor mapping
149
181
  const getOnrampVendor = (paymentMethod: FiatPaymentMethod): "coinbase" | "stripe" | "stripe-web2" | undefined => {
150
182
  switch (paymentMethod) {
@@ -165,8 +197,8 @@ export function useAnyspendFlow({
165
197
  srcChain: paymentType === "fiat" ? base.id : selectedSrcChainId,
166
198
  dstChain: isDepositMode ? base.id : selectedDstChainId, // For deposits, always Base; for swaps, use selected destination
167
199
  srcTokenAddress: paymentType === "fiat" ? USDC_BASE.address : selectedSrcToken.address,
168
- dstTokenAddress: isDepositMode ? B3_TOKEN.address : selectedSrcToken.address, // For deposits, always B3
169
- type: "hype_duel",
200
+ dstTokenAddress: selectedDstToken.address,
201
+ type: orderType,
170
202
  amount: activeInputAmountInWei,
171
203
  recipientAddress: selectedRecipientAddress,
172
204
  onrampVendor: paymentType === "fiat" ? getOnrampVendor(selectedFiatPaymentMethod) : undefined,
@@ -253,7 +285,7 @@ export function useAnyspendFlow({
253
285
  const formattedActualDstAmount = amount
254
286
  ? formatTokenAmount(BigInt(amount), oat.data.order.metadata.dstToken.decimals)
255
287
  : undefined;
256
- onTransactionSuccess?.(formattedActualDstAmount);
288
+ onTransactionSuccess?.(formattedActualDstAmount ?? "");
257
289
  }
258
290
  }, [
259
291
  oat?.data?.order.status,
@@ -272,10 +304,11 @@ export function useAnyspendFlow({
272
304
  // Token state
273
305
  selectedSrcChainId,
274
306
  setSelectedSrcChainId,
275
- selectedDstChainId,
276
- setSelectedDstChainId,
307
+ selectedDstChainId, // Derived, not stateful
277
308
  selectedSrcToken,
278
309
  setSelectedSrcToken,
310
+ selectedDstToken,
311
+ setSelectedDstToken,
279
312
  srcAmount,
280
313
  setSrcAmount,
281
314
  dstAmount,
@@ -27,7 +27,7 @@ export function useAnyspendQuote(req: GetQuoteRequest): UseAnyspendQuoteResult {
27
27
  req.srcTokenAddress &&
28
28
  req.dstTokenAddress &&
29
29
  BigInt(
30
- req.type === "swap" || req.type === "hype_duel"
30
+ req.type === "swap" || req.type === "hype_duel" || req.type === "x402_swap" || req.type === "custom_exact_in"
31
31
  ? req.amount
32
32
  : req.type === "mint_nft"
33
33
  ? req.price
@@ -0,0 +1,72 @@
1
+ import { useEffect } from "react";
2
+ import { CryptoPaymentMethodType } from "../components/common/CryptoPaymentMethod";
3
+ import { useConnectedWalletDisplay } from "./useConnectedWalletDisplay";
4
+
5
+ interface UseAutoSelectCryptoPaymentMethodParams {
6
+ /** Current payment type (crypto or fiat) */
7
+ paymentType?: "crypto" | "fiat";
8
+ /** Currently selected payment method */
9
+ selectedCryptoPaymentMethod: CryptoPaymentMethodType;
10
+ /** Function to update the selected payment method */
11
+ setSelectedCryptoPaymentMethod: (method: CryptoPaymentMethodType) => void;
12
+ /** Whether user has enough balance to pay */
13
+ hasEnoughBalance: boolean;
14
+ /** Whether balance is still loading */
15
+ isBalanceLoading: boolean;
16
+ }
17
+
18
+ /**
19
+ * Custom hook to automatically select appropriate crypto payment method
20
+ * based on available wallets and balance.
21
+ *
22
+ * Auto-selection logic:
23
+ * - Only auto-selects when payment method is NONE (doesn't override user choices)
24
+ * - If EOA/Wagmi wallet connected + has balance → CONNECT_WALLET
25
+ * - If EOA/Wagmi wallet connected + insufficient balance → TRANSFER_CRYPTO
26
+ * - If only Global wallet available → GLOBAL_WALLET
27
+ * - If no wallets → remains NONE
28
+ */
29
+ export function useAutoSelectCryptoPaymentMethod({
30
+ paymentType = "crypto",
31
+ selectedCryptoPaymentMethod,
32
+ setSelectedCryptoPaymentMethod,
33
+ hasEnoughBalance,
34
+ isBalanceLoading,
35
+ }: UseAutoSelectCryptoPaymentMethodParams) {
36
+ // Get suggested payment method based on available wallets
37
+ const { suggestedPaymentMethod } = useConnectedWalletDisplay(selectedCryptoPaymentMethod);
38
+
39
+ useEffect(() => {
40
+ // Only auto-select when on crypto payment type and payment method is NONE
41
+ if (paymentType !== "crypto" || selectedCryptoPaymentMethod !== CryptoPaymentMethodType.NONE) {
42
+ return;
43
+ }
44
+
45
+ // If we have a suggested payment method (wallet is connected), use it
46
+ if (suggestedPaymentMethod !== CryptoPaymentMethodType.NONE) {
47
+ // If we have balance info and enough balance, use CONNECT_WALLET
48
+ // Otherwise, default to TRANSFER_CRYPTO if balance is insufficient
49
+ if (!isBalanceLoading) {
50
+ if (hasEnoughBalance && suggestedPaymentMethod === CryptoPaymentMethodType.CONNECT_WALLET) {
51
+ setSelectedCryptoPaymentMethod(CryptoPaymentMethodType.CONNECT_WALLET);
52
+ } else if (!hasEnoughBalance && suggestedPaymentMethod === CryptoPaymentMethodType.CONNECT_WALLET) {
53
+ // Wallet connected but insufficient balance - suggest transfer
54
+ setSelectedCryptoPaymentMethod(CryptoPaymentMethodType.TRANSFER_CRYPTO);
55
+ } else {
56
+ // Use suggested method (e.g., GLOBAL_WALLET)
57
+ setSelectedCryptoPaymentMethod(suggestedPaymentMethod);
58
+ }
59
+ } else {
60
+ // Balance still loading, use suggested method
61
+ setSelectedCryptoPaymentMethod(suggestedPaymentMethod);
62
+ }
63
+ }
64
+ }, [
65
+ paymentType,
66
+ selectedCryptoPaymentMethod,
67
+ suggestedPaymentMethod,
68
+ hasEnoughBalance,
69
+ isBalanceLoading,
70
+ setSelectedCryptoPaymentMethod,
71
+ ]);
72
+ }
@@ -0,0 +1,80 @@
1
+ import { client } from "@b3dotfun/sdk/shared/utils/thirdweb";
2
+ import { useCallback, useEffect, useRef } from "react";
3
+ import { useSetActiveWallet } from "thirdweb/react";
4
+ import { WalletId, createWallet } from "thirdweb/wallets";
5
+ import { useAccount } from "wagmi";
6
+
7
+ /**
8
+ * Hook that automatically sets the active thirdweb wallet when a wagmi wallet connects.
9
+ *
10
+ * This is useful for syncing wagmi wallet connections with thirdweb's wallet system,
11
+ * ensuring that when users connect via wagmi, the active wallet is properly set.
12
+ *
13
+ * Place this hook in components that stay mounted throughout the user flow
14
+ * (not in components that unmount during navigation).
15
+ */
16
+ export function useAutoSetActiveWalletFromWagmi() {
17
+ const { address: wagmiAddress, connector: wagmiConnector } = useAccount();
18
+ const setActiveWallet = useSetActiveWallet();
19
+ const prevWagmiAddress = useRef<string | undefined>(undefined);
20
+
21
+ // Map wagmi connector names to thirdweb wallet IDs
22
+ const getThirdwebWalletId = useCallback((connectorName: string): WalletId | null => {
23
+ const walletMap: Record<string, WalletId> = {
24
+ MetaMask: "io.metamask",
25
+ "Coinbase Wallet": "com.coinbase.wallet",
26
+ Rainbow: "me.rainbow",
27
+ WalletConnect: "walletConnect",
28
+ Phantom: "app.phantom",
29
+ };
30
+ return walletMap[connectorName] || null;
31
+ }, []);
32
+
33
+ // Create thirdweb wallet from wagmi connector
34
+ const createThirdwebWalletFromConnector = useCallback(
35
+ async (connectorName: string) => {
36
+ const walletId = getThirdwebWalletId(connectorName);
37
+ if (!walletId) {
38
+ console.warn(`No thirdweb wallet ID found for connector: ${connectorName}`);
39
+ return null;
40
+ }
41
+
42
+ try {
43
+ const thirdwebWallet = createWallet(walletId);
44
+ await thirdwebWallet.connect({ client });
45
+ return thirdwebWallet;
46
+ } catch (error) {
47
+ console.error(`Failed to create thirdweb wallet for ${connectorName}:`, error);
48
+ return null;
49
+ }
50
+ },
51
+ [getThirdwebWalletId],
52
+ );
53
+
54
+ // Listen for wagmi wallet connections and automatically set active wallet
55
+ useEffect(() => {
56
+ const isNewConnection = wagmiAddress && wagmiAddress !== prevWagmiAddress.current;
57
+
58
+ if (isNewConnection && wagmiConnector?.name) {
59
+ prevWagmiAddress.current = wagmiAddress;
60
+
61
+ const setupThirdwebWallet = async () => {
62
+ try {
63
+ const thirdwebWallet = await createThirdwebWalletFromConnector(wagmiConnector.name);
64
+ if (thirdwebWallet) {
65
+ setActiveWallet(thirdwebWallet);
66
+ console.log(`Auto-set active wallet for ${wagmiConnector.name}`);
67
+ }
68
+ } catch (error) {
69
+ console.error("Failed to auto-set active wallet:", error);
70
+ }
71
+ };
72
+
73
+ setupThirdwebWallet();
74
+ }
75
+
76
+ if (!wagmiAddress) {
77
+ prevWagmiAddress.current = undefined;
78
+ }
79
+ }, [wagmiAddress, wagmiConnector?.name, setActiveWallet, createThirdwebWalletFromConnector]);
80
+ }
@@ -0,0 +1,69 @@
1
+ import { useAccountWallet } from "@b3dotfun/sdk/global-account/react";
2
+ import { useAccount } from "wagmi";
3
+ import { CryptoPaymentMethodType } from "../components/common/CryptoPaymentMethod";
4
+
5
+ interface UseConnectedWalletDisplayResult {
6
+ walletAddress: string | undefined;
7
+ shouldShowConnectedEOA: boolean;
8
+ shouldShowWagmiWallet: boolean;
9
+ isWalletDuplicated: boolean;
10
+ suggestedPaymentMethod: CryptoPaymentMethodType;
11
+ }
12
+
13
+ /**
14
+ * Custom hook to determine which wallet to display and its address
15
+ * Handles logic for showing EOA wallet, wagmi wallet, or global wallet based on payment method
16
+ */
17
+ export function useConnectedWalletDisplay(
18
+ selectedCryptoPaymentMethod?: CryptoPaymentMethodType,
19
+ ): UseConnectedWalletDisplayResult {
20
+ const { connectedEOAWallet, connectedSmartWallet } = useAccountWallet();
21
+ const { address: wagmiAddress, isConnected: wagmiWalletIsConnected } = useAccount();
22
+
23
+ // Helper function to check if two addresses are the same
24
+ const isSameAddress = (addr1?: string, addr2?: string): boolean => {
25
+ if (!addr1 || !addr2) return false;
26
+ return addr1.toLowerCase() === addr2.toLowerCase();
27
+ };
28
+
29
+ // Check if connectedEOAWallet and wagmi wallet represent the same wallet
30
+ const connectedEOAAddress = connectedEOAWallet?.getAccount()?.address;
31
+ const isWalletDuplicated = isSameAddress(connectedEOAAddress, wagmiAddress);
32
+
33
+ // Determine which wallet to show (prefer connectedEOAWallet if both exist and are the same)
34
+ const shouldShowConnectedEOA = !!connectedEOAWallet;
35
+ const shouldShowWagmiWallet = wagmiWalletIsConnected && (!isWalletDuplicated || !connectedEOAWallet);
36
+
37
+ // Determine which address to use based on payment method
38
+ let walletAddress: string | undefined;
39
+
40
+ if (selectedCryptoPaymentMethod === CryptoPaymentMethodType.GLOBAL_WALLET) {
41
+ walletAddress = connectedSmartWallet?.getAccount()?.address;
42
+ } else if (selectedCryptoPaymentMethod === CryptoPaymentMethodType.CONNECT_WALLET) {
43
+ // Prefer connectedEOAWallet, fallback to wagmi wallet
44
+ walletAddress = connectedEOAAddress || wagmiAddress;
45
+ } else {
46
+ // Default behavior: use connectedEOAWallet if available, otherwise wagmi
47
+ walletAddress = connectedEOAAddress || wagmiAddress;
48
+ }
49
+
50
+ // Suggest a payment method based on available wallets
51
+ // Priority: Connected EOA/Wagmi wallet > Global wallet > None
52
+ let suggestedPaymentMethod = CryptoPaymentMethodType.NONE;
53
+
54
+ if (connectedEOAAddress || wagmiAddress) {
55
+ // If there's a connected EOA or wagmi wallet, suggest CONNECT_WALLET
56
+ suggestedPaymentMethod = CryptoPaymentMethodType.CONNECT_WALLET;
57
+ } else if (connectedSmartWallet?.getAccount()?.address) {
58
+ // If only global wallet is available, suggest that
59
+ suggestedPaymentMethod = CryptoPaymentMethodType.GLOBAL_WALLET;
60
+ }
61
+
62
+ return {
63
+ walletAddress,
64
+ shouldShowConnectedEOA,
65
+ shouldShowWagmiWallet,
66
+ isWalletDuplicated,
67
+ suggestedPaymentMethod,
68
+ };
69
+ }
@@ -0,0 +1,301 @@
1
+ import {
2
+ createAssociatedTokenAccountInstruction,
3
+ createTransferCheckedInstruction,
4
+ getAssociatedTokenAddressSync,
5
+ } from "@solana/spl-token";
6
+ import { ComputeBudgetProgram, Connection, PublicKey, SystemProgram, Transaction } from "@solana/web3.js";
7
+ import { useCallback, useMemo } from "react";
8
+ import { toast } from "sonner";
9
+
10
+ interface UsePhantomTransferParams {
11
+ /** RPC endpoint URL for Solana network */
12
+ rpcEndpoint?: string;
13
+ }
14
+
15
+ interface PhantomTransferParams {
16
+ /** Amount in lamports (for SOL) or smallest token unit (for SPL tokens) */
17
+ amountLamports: string;
18
+ /** Token address (use "11111111111111111111111111111111" for native SOL) */
19
+ tokenAddress: string;
20
+ /** Recipient address */
21
+ recipientAddress: string;
22
+ }
23
+
24
+ /**
25
+ * Custom hook for handling Phantom wallet transfers on Solana.
26
+ * Supports both native SOL and SPL token transfers with automatic priority fee calculation.
27
+ *
28
+ * @example
29
+ * ```tsx
30
+ * const { initiateTransfer, isPhantomAvailable } = usePhantomTransfer();
31
+ *
32
+ * await initiateTransfer({
33
+ * amountLamports: "1000000000", // 1 SOL
34
+ * tokenAddress: "11111111111111111111111111111111",
35
+ * recipientAddress: "..."
36
+ * });
37
+ * ```
38
+ */
39
+ export function usePhantomTransfer({ rpcEndpoint }: UsePhantomTransferParams = {}) {
40
+ // Default RPC endpoint
41
+ const defaultRpcEndpoint = "https://mainnet.helius-rpc.com/?api-key=efafd9b3-1807-4cf8-8aa4-3d984f56d8fb";
42
+ const effectiveRpcEndpoint = rpcEndpoint || defaultRpcEndpoint;
43
+
44
+ // Check for Phantom wallet availability
45
+ const isPhantomMobile = useMemo(() => navigator.userAgent.includes("Phantom"), []);
46
+ const isPhantomBrowser = useMemo(() => (window as any).phantom?.solana?.isPhantom, []);
47
+ const isPhantomAvailable = isPhantomMobile || isPhantomBrowser;
48
+
49
+ /**
50
+ * Get the connected Phantom wallet address if available
51
+ */
52
+ const getConnectedAddress = useCallback(() => {
53
+ const phantom = (window as any).phantom?.solana;
54
+ if (phantom?.isConnected && phantom?.publicKey) {
55
+ return phantom.publicKey.toString();
56
+ }
57
+ return null;
58
+ }, []);
59
+
60
+ /**
61
+ * Calculate optimal priority fee based on recent network activity
62
+ */
63
+ const calculatePriorityFee = useCallback(async (connection: Connection, fromPubkey: PublicKey): Promise<number> => {
64
+ let priorityFee = 10000; // Default fallback (10,000 micro-lamports)
65
+
66
+ try {
67
+ const recentFees = await connection.getRecentPrioritizationFees({
68
+ lockedWritableAccounts: [fromPubkey],
69
+ });
70
+
71
+ if (recentFees && recentFees.length > 0) {
72
+ // Use 75th percentile of recent fees for good priority
73
+ const sortedFees = recentFees.map(fee => fee.prioritizationFee).sort((a, b) => a - b);
74
+ const percentile75Index = Math.floor(sortedFees.length * 0.75);
75
+ priorityFee = Math.max(sortedFees[percentile75Index] || 10000, 10000);
76
+ }
77
+ } catch (feeError) {
78
+ console.warn("Failed to fetch recent priority fees, using default:", feeError);
79
+ }
80
+
81
+ return priorityFee;
82
+ }, []);
83
+
84
+ /**
85
+ * Create a native SOL transfer transaction with priority fees
86
+ */
87
+ const createNativeTransferTransaction = useCallback(
88
+ async (
89
+ _connection: Connection,
90
+ fromPubkey: PublicKey,
91
+ toPubkey: PublicKey,
92
+ amount: bigint,
93
+ priorityFee: number,
94
+ ): Promise<Transaction> => {
95
+ const computeUnitLimit = 1000; // SOL transfer + compute budget instructions need ~600-800 CU
96
+ const computeUnitPrice = Math.min(priorityFee, 100000); // Cap at 100k micro-lamports for safety
97
+
98
+ const transaction = new Transaction()
99
+ .add(
100
+ // Set compute unit limit first (must come before other instructions)
101
+ ComputeBudgetProgram.setComputeUnitLimit({
102
+ units: computeUnitLimit,
103
+ }),
104
+ )
105
+ .add(
106
+ // Set priority fee
107
+ ComputeBudgetProgram.setComputeUnitPrice({
108
+ microLamports: computeUnitPrice,
109
+ }),
110
+ )
111
+ .add(
112
+ // Actual transfer instruction
113
+ SystemProgram.transfer({
114
+ fromPubkey,
115
+ toPubkey,
116
+ lamports: Number(amount),
117
+ }),
118
+ );
119
+
120
+ console.log(`Using priority fee: ${computeUnitPrice} micro-lamports per CU, limit: ${computeUnitLimit} CU`);
121
+
122
+ return transaction;
123
+ },
124
+ [],
125
+ );
126
+
127
+ /**
128
+ * Create an SPL token transfer transaction with priority fees
129
+ */
130
+ const createSPLTransferTransaction = useCallback(
131
+ async (
132
+ connection: Connection,
133
+ fromPubkey: PublicKey,
134
+ toPubkey: PublicKey,
135
+ mintPubkey: PublicKey,
136
+ amount: bigint,
137
+ priorityFee: number,
138
+ ): Promise<Transaction> => {
139
+ // Get associated token accounts
140
+ const fromTokenAccount = getAssociatedTokenAddressSync(mintPubkey, fromPubkey);
141
+ const toTokenAccount = getAssociatedTokenAddressSync(mintPubkey, toPubkey);
142
+
143
+ // Check if destination token account exists
144
+ const toTokenAccountInfo = await connection.getAccountInfo(toTokenAccount);
145
+ const needsDestinationAccount = !toTokenAccountInfo;
146
+
147
+ // Get mint info to determine decimals
148
+ const mintInfo = await connection.getParsedAccountInfo(mintPubkey);
149
+ const decimals = (mintInfo.value?.data as any)?.parsed?.info?.decimals || 9;
150
+
151
+ // SPL transfers need more compute units than SOL transfers
152
+ // Add extra CU if we need to create destination account
153
+ const computeUnitLimit = needsDestinationAccount ? 40000 : 20000;
154
+ const computeUnitPrice = Math.min(priorityFee, 100000);
155
+
156
+ // Create transfer instruction
157
+ const transferInstruction = createTransferCheckedInstruction(
158
+ fromTokenAccount,
159
+ mintPubkey,
160
+ toTokenAccount,
161
+ fromPubkey,
162
+ Number(amount),
163
+ decimals,
164
+ );
165
+
166
+ const transaction = new Transaction()
167
+ .add(
168
+ ComputeBudgetProgram.setComputeUnitLimit({
169
+ units: computeUnitLimit,
170
+ }),
171
+ )
172
+ .add(
173
+ ComputeBudgetProgram.setComputeUnitPrice({
174
+ microLamports: computeUnitPrice,
175
+ }),
176
+ );
177
+
178
+ // Add create destination account instruction if needed
179
+ if (needsDestinationAccount) {
180
+ transaction.add(
181
+ createAssociatedTokenAccountInstruction(
182
+ fromPubkey, // payer
183
+ toTokenAccount, // ata
184
+ toPubkey, // owner
185
+ mintPubkey, // mint
186
+ ),
187
+ );
188
+ }
189
+
190
+ // Add the transfer instruction
191
+ transaction.add(transferInstruction);
192
+
193
+ console.log(
194
+ `SPL Token transfer: ${computeUnitPrice} micro-lamports per CU, limit: ${computeUnitLimit} CU, creating destination: ${needsDestinationAccount}`,
195
+ );
196
+
197
+ return transaction;
198
+ },
199
+ [],
200
+ );
201
+
202
+ /**
203
+ * Initiate a Phantom wallet transfer for SOL or SPL tokens
204
+ */
205
+ const initiateTransfer = useCallback(
206
+ async ({ amountLamports, tokenAddress, recipientAddress }: PhantomTransferParams): Promise<void> => {
207
+ try {
208
+ // Step 1: Check if Phantom is installed
209
+ if (!isPhantomAvailable) {
210
+ toast.error("Phantom wallet not installed. Please install Phantom wallet to continue.");
211
+ return;
212
+ }
213
+
214
+ // Step 2: Ensure Phantom is connected/unlocked
215
+ const phantom = (window as any).phantom?.solana;
216
+ if (!phantom) {
217
+ toast.error("Phantom wallet not accessible");
218
+ return;
219
+ }
220
+
221
+ // Connect and unlock wallet if needed
222
+ let publicKey;
223
+ try {
224
+ const connection = await phantom.connect();
225
+ publicKey = connection.publicKey;
226
+ } catch (connectError) {
227
+ toast.error("Failed to connect to Phantom wallet");
228
+ return;
229
+ }
230
+
231
+ // Step 3: Setup connection and public keys
232
+ const connection = new Connection(effectiveRpcEndpoint);
233
+ const fromPubkey = new PublicKey(publicKey.toString());
234
+ const toPubkey = new PublicKey(recipientAddress);
235
+ const amount = BigInt(amountLamports);
236
+
237
+ // Step 4: Calculate optimal priority fee
238
+ const priorityFee = await calculatePriorityFee(connection, fromPubkey);
239
+
240
+ // Step 5: Create transaction based on token type
241
+ let transaction: Transaction;
242
+
243
+ if (tokenAddress === "11111111111111111111111111111111") {
244
+ // Native SOL transfer
245
+ transaction = await createNativeTransferTransaction(connection, fromPubkey, toPubkey, amount, priorityFee);
246
+ } else {
247
+ // SPL Token transfer
248
+ const mintPubkey = new PublicKey(tokenAddress);
249
+ transaction = await createSPLTransferTransaction(
250
+ connection,
251
+ fromPubkey,
252
+ toPubkey,
253
+ mintPubkey,
254
+ amount,
255
+ priorityFee,
256
+ );
257
+ }
258
+
259
+ // Step 6: Get latest blockhash and set fee payer
260
+ const { blockhash } = await connection.getLatestBlockhash("confirmed");
261
+ transaction.recentBlockhash = blockhash;
262
+ transaction.feePayer = fromPubkey;
263
+
264
+ // Step 7: Sign and send transaction
265
+ const signedTransaction = await phantom.signAndSendTransaction(transaction);
266
+
267
+ toast.success(`Transaction successful! Signature: ${signedTransaction.signature}`);
268
+ console.log("Transaction sent with priority fees. Signature:", signedTransaction.signature);
269
+ } catch (error: unknown) {
270
+ console.error("Transfer error:", error);
271
+ const errorMessage = error instanceof Error ? error.message : String(error);
272
+
273
+ if (errorMessage.includes("User rejected")) {
274
+ toast.error("Transaction was cancelled by user");
275
+ } else if (errorMessage.includes("insufficient")) {
276
+ toast.error("Insufficient balance for this transaction");
277
+ } else if (errorMessage.includes("blockhash not found")) {
278
+ toast.error("Network congestion detected. Please try again in a moment.");
279
+ } else {
280
+ toast.error(`Transfer failed: ${errorMessage}`);
281
+ }
282
+ }
283
+ },
284
+ [
285
+ isPhantomAvailable,
286
+ effectiveRpcEndpoint,
287
+ calculatePriorityFee,
288
+ createNativeTransferTransaction,
289
+ createSPLTransferTransaction,
290
+ ],
291
+ );
292
+
293
+ return {
294
+ /** Function to initiate a transfer */
295
+ initiateTransfer,
296
+ /** Whether Phantom wallet is available (installed) */
297
+ isPhantomAvailable,
298
+ /** Get the currently connected Phantom wallet address */
299
+ getConnectedAddress,
300
+ };
301
+ }