@b3dotfun/sdk 0.0.62 → 0.0.63-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 (171) 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 +17 -4
  38. package/dist/cjs/global-account/react/components/ManageAccount/BalanceContent.js +3 -3
  39. package/dist/cjs/global-account/react/components/ProfileEditor/ProfileEditor.d.ts +6 -0
  40. package/dist/cjs/global-account/react/components/ProfileEditor/ProfileEditor.js +141 -0
  41. package/dist/cjs/global-account/react/components/SignInWithB3/SignInWithB3Flow.js +3 -1
  42. package/dist/cjs/global-account/react/components/SignInWithB3/steps/LoginStep.js +2 -2
  43. package/dist/cjs/global-account/react/components/index.d.ts +2 -0
  44. package/dist/cjs/global-account/react/components/index.js +7 -2
  45. package/dist/cjs/global-account/react/hooks/index.d.ts +2 -1
  46. package/dist/cjs/global-account/react/hooks/index.js +5 -3
  47. package/dist/cjs/global-account/react/hooks/useAuthentication.d.ts +2 -2
  48. package/dist/cjs/global-account/react/hooks/useAuthentication.js +7 -2
  49. package/dist/cjs/global-account/react/hooks/useSimBalance.d.ts +1 -1
  50. package/dist/cjs/global-account/react/hooks/useSimBalance.js +6 -5
  51. package/dist/cjs/global-account/react/hooks/useTokenBalanceDirect.d.ts +12 -0
  52. package/dist/cjs/global-account/react/hooks/useTokenBalanceDirect.js +62 -0
  53. package/dist/cjs/global-account/react/hooks/useTokenFromUrl.js +4 -3
  54. package/dist/cjs/global-account/react/stores/useModalStore.d.ts +37 -1
  55. package/dist/cjs/global-account/react/utils/profileDisplay.d.ts +6 -0
  56. package/dist/cjs/global-account/react/utils/profileDisplay.js +60 -4
  57. package/dist/esm/anyspend/react/components/AnySpend.js +62 -24
  58. package/dist/esm/anyspend/react/components/AnySpendCustom.js +3 -0
  59. package/dist/esm/anyspend/react/components/AnySpendCustomExactIn.d.ts +34 -0
  60. package/dist/esm/anyspend/react/components/AnySpendCustomExactIn.js +269 -0
  61. package/dist/esm/anyspend/react/components/AnySpendStakeB3.js +7 -6
  62. package/dist/esm/anyspend/react/components/AnySpendStakeB3ExactIn.d.ts +9 -0
  63. package/dist/esm/anyspend/react/components/AnySpendStakeB3ExactIn.js +285 -0
  64. package/dist/esm/anyspend/react/components/AnySpendStakeUpsideExactIn.d.ts +11 -0
  65. package/dist/esm/anyspend/react/components/AnySpendStakeUpsideExactIn.js +30 -0
  66. package/dist/esm/anyspend/react/components/AnyspendDepositHype.js +4 -4
  67. package/dist/esm/anyspend/react/components/common/CryptoPaySection.js +5 -7
  68. package/dist/esm/anyspend/react/components/common/CryptoPaymentMethod.js +9 -17
  69. package/dist/esm/anyspend/react/components/common/CryptoReceiveSection.d.ts +6 -1
  70. package/dist/esm/anyspend/react/components/common/CryptoReceiveSection.js +11 -1
  71. package/dist/esm/anyspend/react/components/common/OrderDetails.js +67 -148
  72. package/dist/esm/anyspend/react/components/common/OrderDetailsCollapsible.js +2 -3
  73. package/dist/esm/anyspend/react/components/common/OrderTokenAmount.d.ts +2 -1
  74. package/dist/esm/anyspend/react/components/common/OrderTokenAmount.js +40 -16
  75. package/dist/esm/anyspend/react/components/common/PaySection.js +1 -1
  76. package/dist/esm/anyspend/react/components/common/TokenBalance.js +2 -2
  77. package/dist/esm/anyspend/react/components/index.d.ts +5 -1
  78. package/dist/esm/anyspend/react/components/index.js +5 -1
  79. package/dist/esm/anyspend/react/hooks/useAnyspendFlow.d.ts +25 -3
  80. package/dist/esm/anyspend/react/hooks/useAnyspendFlow.js +42 -19
  81. package/dist/esm/anyspend/react/hooks/useAnyspendOrderHistory.d.ts +116 -0
  82. package/dist/esm/anyspend/react/hooks/useAnyspendQuote.js +1 -1
  83. package/dist/esm/anyspend/react/hooks/useAutoSelectCryptoPaymentMethod.d.ts +26 -0
  84. package/dist/esm/anyspend/react/hooks/useAutoSelectCryptoPaymentMethod.js +53 -0
  85. package/dist/esm/anyspend/react/hooks/useAutoSetActiveWalletFromWagmi.d.ts +10 -0
  86. package/dist/esm/anyspend/react/hooks/useAutoSetActiveWalletFromWagmi.js +70 -0
  87. package/dist/esm/anyspend/react/hooks/useConnectedWalletDisplay.d.ts +14 -0
  88. package/dist/esm/anyspend/react/hooks/useConnectedWalletDisplay.js +54 -0
  89. package/dist/esm/anyspend/react/hooks/usePhantomTransfer.d.ts +36 -0
  90. package/dist/esm/anyspend/react/hooks/usePhantomTransfer.js +208 -0
  91. package/dist/esm/anyspend/types/api.d.ts +665 -3
  92. package/dist/esm/anyspend/utils/orderPayload.js +4 -0
  93. package/dist/esm/global-account/react/components/B3DynamicModal.js +18 -5
  94. package/dist/esm/global-account/react/components/ManageAccount/BalanceContent.js +3 -3
  95. package/dist/esm/global-account/react/components/ProfileEditor/ProfileEditor.d.ts +6 -0
  96. package/dist/esm/global-account/react/components/ProfileEditor/ProfileEditor.js +135 -0
  97. package/dist/esm/global-account/react/components/SignInWithB3/SignInWithB3Flow.js +3 -1
  98. package/dist/esm/global-account/react/components/SignInWithB3/steps/LoginStep.js +2 -2
  99. package/dist/esm/global-account/react/components/index.d.ts +2 -0
  100. package/dist/esm/global-account/react/components/index.js +3 -0
  101. package/dist/esm/global-account/react/hooks/index.d.ts +2 -1
  102. package/dist/esm/global-account/react/hooks/index.js +2 -1
  103. package/dist/esm/global-account/react/hooks/useAuthentication.d.ts +2 -2
  104. package/dist/esm/global-account/react/hooks/useAuthentication.js +7 -2
  105. package/dist/esm/global-account/react/hooks/useSimBalance.d.ts +1 -1
  106. package/dist/esm/global-account/react/hooks/useSimBalance.js +6 -5
  107. package/dist/esm/global-account/react/hooks/useTokenBalanceDirect.d.ts +12 -0
  108. package/dist/esm/global-account/react/hooks/useTokenBalanceDirect.js +59 -0
  109. package/dist/esm/global-account/react/hooks/useTokenFromUrl.js +4 -3
  110. package/dist/esm/global-account/react/stores/useModalStore.d.ts +37 -1
  111. package/dist/esm/global-account/react/utils/profileDisplay.d.ts +6 -0
  112. package/dist/esm/global-account/react/utils/profileDisplay.js +59 -4
  113. package/dist/types/anyspend/react/components/AnySpendCustomExactIn.d.ts +34 -0
  114. package/dist/types/anyspend/react/components/AnySpendStakeB3ExactIn.d.ts +9 -0
  115. package/dist/types/anyspend/react/components/AnySpendStakeUpsideExactIn.d.ts +11 -0
  116. package/dist/types/anyspend/react/components/common/CryptoReceiveSection.d.ts +6 -1
  117. package/dist/types/anyspend/react/components/common/OrderTokenAmount.d.ts +2 -1
  118. package/dist/types/anyspend/react/components/index.d.ts +5 -1
  119. package/dist/types/anyspend/react/hooks/useAnyspendFlow.d.ts +25 -3
  120. package/dist/types/anyspend/react/hooks/useAnyspendOrderHistory.d.ts +116 -0
  121. package/dist/types/anyspend/react/hooks/useAutoSelectCryptoPaymentMethod.d.ts +26 -0
  122. package/dist/types/anyspend/react/hooks/useAutoSetActiveWalletFromWagmi.d.ts +10 -0
  123. package/dist/types/anyspend/react/hooks/useConnectedWalletDisplay.d.ts +14 -0
  124. package/dist/types/anyspend/react/hooks/usePhantomTransfer.d.ts +36 -0
  125. package/dist/types/anyspend/types/api.d.ts +665 -3
  126. package/dist/types/global-account/react/components/ProfileEditor/ProfileEditor.d.ts +6 -0
  127. package/dist/types/global-account/react/components/index.d.ts +2 -0
  128. package/dist/types/global-account/react/hooks/index.d.ts +2 -1
  129. package/dist/types/global-account/react/hooks/useAuthentication.d.ts +2 -2
  130. package/dist/types/global-account/react/hooks/useSimBalance.d.ts +1 -1
  131. package/dist/types/global-account/react/hooks/useTokenBalanceDirect.d.ts +12 -0
  132. package/dist/types/global-account/react/stores/useModalStore.d.ts +37 -1
  133. package/dist/types/global-account/react/utils/profileDisplay.d.ts +6 -0
  134. package/package.json +4 -3
  135. package/src/anyspend/react/components/AnySpend.tsx +73 -22
  136. package/src/anyspend/react/components/AnySpendCustom.tsx +4 -0
  137. package/src/anyspend/react/components/AnySpendCustomExactIn.tsx +595 -0
  138. package/src/anyspend/react/components/AnySpendStakeB3.tsx +8 -11
  139. package/src/anyspend/react/components/AnySpendStakeB3ExactIn.tsx +522 -0
  140. package/src/anyspend/react/components/AnySpendStakeUpsideExactIn.tsx +73 -0
  141. package/src/anyspend/react/components/AnyspendDepositHype.tsx +7 -3
  142. package/src/anyspend/react/components/common/CryptoPaySection.tsx +5 -7
  143. package/src/anyspend/react/components/common/CryptoPaymentMethod.tsx +9 -18
  144. package/src/anyspend/react/components/common/CryptoReceiveSection.tsx +22 -0
  145. package/src/anyspend/react/components/common/OrderDetails.tsx +76 -190
  146. package/src/anyspend/react/components/common/OrderDetailsCollapsible.tsx +2 -3
  147. package/src/anyspend/react/components/common/OrderTokenAmount.tsx +48 -17
  148. package/src/anyspend/react/components/common/PaySection.tsx +1 -0
  149. package/src/anyspend/react/components/common/TokenBalance.tsx +2 -2
  150. package/src/anyspend/react/components/index.ts +5 -1
  151. package/src/anyspend/react/hooks/useAnyspendFlow.ts +51 -18
  152. package/src/anyspend/react/hooks/useAnyspendQuote.ts +1 -1
  153. package/src/anyspend/react/hooks/useAutoSelectCryptoPaymentMethod.ts +72 -0
  154. package/src/anyspend/react/hooks/useAutoSetActiveWalletFromWagmi.ts +80 -0
  155. package/src/anyspend/react/hooks/useConnectedWalletDisplay.ts +69 -0
  156. package/src/anyspend/react/hooks/usePhantomTransfer.ts +301 -0
  157. package/src/anyspend/types/api.ts +669 -1
  158. package/src/anyspend/utils/orderPayload.ts +5 -1
  159. package/src/global-account/react/components/B3DynamicModal.tsx +18 -4
  160. package/src/global-account/react/components/ManageAccount/BalanceContent.tsx +4 -4
  161. package/src/global-account/react/components/ProfileEditor/ProfileEditor.tsx +265 -0
  162. package/src/global-account/react/components/SignInWithB3/SignInWithB3Flow.tsx +3 -1
  163. package/src/global-account/react/components/SignInWithB3/steps/LoginStep.tsx +2 -2
  164. package/src/global-account/react/components/index.ts +4 -0
  165. package/src/global-account/react/hooks/index.ts +2 -1
  166. package/src/global-account/react/hooks/useAuthentication.ts +10 -2
  167. package/src/global-account/react/hooks/useSimBalance.ts +6 -5
  168. package/src/global-account/react/hooks/useTokenBalanceDirect.tsx +84 -0
  169. package/src/global-account/react/hooks/useTokenFromUrl.tsx +6 -5
  170. package/src/global-account/react/stores/useModalStore.ts +43 -1
  171. package/src/global-account/react/utils/profileDisplay.ts +67 -4
@@ -0,0 +1,522 @@
1
+ import { ABI_ERC20_STAKING, B3_TOKEN, eqci } from "@b3dotfun/sdk/anyspend";
2
+ import { normalizeAddress } from "@b3dotfun/sdk/anyspend/utils";
3
+ import {
4
+ Button,
5
+ GlareCardRounded,
6
+ Input,
7
+ StyleRoot,
8
+ TextLoop,
9
+ useHasMounted,
10
+ useModalStore,
11
+ useSimBalance,
12
+ useUnifiedChainSwitchAndExecute,
13
+ } from "@b3dotfun/sdk/global-account/react";
14
+ import { PUBLIC_BASE_RPC_URL } from "@b3dotfun/sdk/shared/constants";
15
+ import { formatTokenAmount } from "@b3dotfun/sdk/shared/utils/number";
16
+ import { ArrowRight, Loader2 } from "lucide-react";
17
+ import { motion } from "motion/react";
18
+ import { useEffect, useState } from "react";
19
+ import { toast } from "sonner";
20
+ import { createPublicClient, encodeFunctionData, erc20Abi, http } from "viem";
21
+ import { base } from "viem/chains";
22
+ import { useAccount, useWaitForTransactionReceipt } from "wagmi";
23
+ import { AnySpendCustomExactIn } from "./AnySpendCustomExactIn";
24
+ import { EthIcon } from "./icons/EthIcon";
25
+ import { SolIcon } from "./icons/SolIcon";
26
+ import { UsdcIcon } from "./icons/USDCIcon";
27
+
28
+ const basePublicClient = createPublicClient({
29
+ chain: base,
30
+ transport: http(PUBLIC_BASE_RPC_URL),
31
+ });
32
+
33
+ const ERC20Staking = "0xbf04200be3cbf371467a539706393c81c470f523";
34
+
35
+ const STAKE_FUNCTION_ABI = JSON.stringify([
36
+ {
37
+ name: "stake",
38
+ type: "function",
39
+ stateMutability: "nonpayable",
40
+ inputs: [
41
+ { name: "amount", type: "uint256" },
42
+ { name: "beneficiary", type: "address" },
43
+ ],
44
+ outputs: [],
45
+ },
46
+ ]);
47
+
48
+ export function AnySpendStakeB3ExactIn({
49
+ loadOrder,
50
+ mode = "modal",
51
+ sourceTokenAddress,
52
+ sourceTokenChainId,
53
+ recipientAddress,
54
+ stakeAmount,
55
+ onSuccess,
56
+ }: {
57
+ loadOrder?: string;
58
+ mode?: "modal" | "page";
59
+ sourceTokenAddress?: string;
60
+ sourceTokenChainId?: number;
61
+ recipientAddress: string;
62
+ stakeAmount?: string;
63
+ onSuccess?: (amount: string) => void;
64
+ }) {
65
+ const hasMounted = useHasMounted();
66
+ const { setB3ModalOpen } = useModalStore();
67
+
68
+ // Wagmi hooks for direct staking
69
+ const { address } = useAccount();
70
+ const { switchChainAndExecute, isSwitchingOrExecuting } = useUnifiedChainSwitchAndExecute();
71
+
72
+ // Fetch B3 token balance
73
+ const { data: simBalance, isLoading: isBalanceLoading } = useSimBalance(address, [base.id]);
74
+ const b3RawBalanceStr = simBalance?.balances.find(b => eqci(b.address, B3_TOKEN.address))?.amount || "0";
75
+ const b3RawBalance = BigInt(b3RawBalanceStr);
76
+ const b3Balance = formatTokenAmount(b3RawBalance, B3_TOKEN.decimals);
77
+
78
+ // State for direct staking flow
79
+ const [isStaking, setIsStaking] = useState(false);
80
+ const [stakingTxHash, setStakingTxHash] = useState<string>("");
81
+ const [showSuccessModal, setShowSuccessModal] = useState(false);
82
+
83
+ // Wait for transaction confirmation
84
+ const { isLoading: isTxPending, isSuccess: isTxSuccess } = useWaitForTransactionReceipt({
85
+ hash: stakingTxHash as `0x${string}`,
86
+ query: {
87
+ structuralSharing: false, // Disable to avoid BigInt serialization issues
88
+ },
89
+ });
90
+
91
+ // Show success modal when transaction is confirmed
92
+ useEffect(() => {
93
+ if (isTxSuccess && stakingTxHash) {
94
+ setShowAmountPrompt(false);
95
+ setShowSuccessModal(true);
96
+ setIsStaking(false);
97
+ }
98
+ }, [isTxSuccess, stakingTxHash]);
99
+
100
+ const [userStakeAmount, setUserStakeAmount] = useState<string>(stakeAmount || "");
101
+ const [showAmountPrompt, setShowAmountPrompt] = useState<boolean>(!stakeAmount);
102
+ const [isAmountValid, setIsAmountValid] = useState<boolean>(!!stakeAmount);
103
+ const [validationError, setValidationError] = useState<string>("");
104
+ // Store display amount for UI
105
+ const [displayAmount, setDisplayAmount] = useState<string>("");
106
+ // Debounced state for balance checks and messaging
107
+ const [debouncedAmount, setDebouncedAmount] = useState<string>("");
108
+ const [debouncedUserStakeAmount, setDebouncedUserStakeAmount] = useState<string>("");
109
+
110
+ // Debounce the amount for balance checks
111
+ useEffect(() => {
112
+ const timer = setTimeout(() => {
113
+ setDebouncedAmount(displayAmount);
114
+ setDebouncedUserStakeAmount(userStakeAmount);
115
+ }, 500);
116
+
117
+ return () => clearTimeout(timer);
118
+ }, [displayAmount, userStakeAmount]);
119
+
120
+ useEffect(() => {
121
+ if (stakeAmount) {
122
+ setUserStakeAmount(stakeAmount);
123
+ setShowAmountPrompt(false);
124
+ setIsAmountValid(true);
125
+ }
126
+ }, [stakeAmount]);
127
+
128
+ if (!recipientAddress) return null;
129
+
130
+ const validateAndSetAmount = (value: string) => {
131
+ // Allow decimal input by validating against a pattern
132
+ // This regex allows numbers with up to 18 decimal places
133
+ const isValidFormat = /^(\d+\.?\d{0,18}|\.\d{1,18})$/.test(value) || value === "";
134
+
135
+ if (!isValidFormat && value !== "") {
136
+ return;
137
+ }
138
+
139
+ setDisplayAmount(value);
140
+
141
+ try {
142
+ if (value === "" || value === ".") {
143
+ setUserStakeAmount("");
144
+ setIsAmountValid(false);
145
+ setValidationError("");
146
+ return;
147
+ }
148
+
149
+ // For UI validation - check if it's a positive number
150
+ const numValue = parseFloat(value);
151
+ if (isNaN(numValue) || numValue <= 0) {
152
+ setIsAmountValid(false);
153
+ setUserStakeAmount("");
154
+ setValidationError("Please enter a valid positive number");
155
+ return;
156
+ }
157
+
158
+ // Check minimum stake amount (50 B3)
159
+ if (numValue < 50) {
160
+ setIsAmountValid(false);
161
+ setUserStakeAmount("");
162
+ setValidationError("Minimum stake amount is 50 B3");
163
+ return;
164
+ }
165
+
166
+ // Convert to wei (multiply by 10^18)
167
+ // Handle decimal places correctly by removing the decimal point
168
+ let fullAmount;
169
+ if (value.includes(".")) {
170
+ const [whole, fraction = ""] = value.split(".");
171
+ // Pad with zeros to 18 decimal places
172
+ const paddedFraction = fraction.padEnd(18, "0");
173
+ fullAmount = whole + paddedFraction;
174
+ } else {
175
+ fullAmount = value + "000000000000000000"; // Add 18 zeros
176
+ }
177
+
178
+ // Remove leading zeros
179
+ fullAmount = fullAmount.replace(/^0+/, "") || "0";
180
+
181
+ // Set the full amount for the actual transaction
182
+ setUserStakeAmount(fullAmount);
183
+ setIsAmountValid(true);
184
+ setValidationError("");
185
+ } catch (error) {
186
+ setIsAmountValid(false);
187
+ setUserStakeAmount("");
188
+ setValidationError("Please enter a valid amount");
189
+ }
190
+ };
191
+
192
+ const handleDirectStaking = async () => {
193
+ if (!address || !basePublicClient || !userStakeAmount) return;
194
+
195
+ try {
196
+ setIsStaking(true);
197
+
198
+ // Check current allowance
199
+ const allowance = await basePublicClient.readContract({
200
+ address: B3_TOKEN.address as `0x${string}`,
201
+ abi: erc20Abi,
202
+ functionName: "allowance",
203
+ args: [address, ERC20Staking as `0x${string}`],
204
+ });
205
+
206
+ // If allowance is insufficient, request approval first
207
+ if (allowance < BigInt(userStakeAmount)) {
208
+ toast.info("Approving B3 spending...");
209
+
210
+ const approvalData = encodeFunctionData({
211
+ abi: erc20Abi,
212
+ functionName: "approve",
213
+ args: [ERC20Staking as `0x${string}`, BigInt(userStakeAmount)],
214
+ });
215
+
216
+ const approvalHash = await switchChainAndExecute(base.id, {
217
+ to: B3_TOKEN.address as `0x${string}`,
218
+ data: approvalData,
219
+ value: BigInt(0),
220
+ });
221
+
222
+ if (!approvalHash) {
223
+ toast.error("Approval failed. Please try again.");
224
+ return;
225
+ }
226
+
227
+ const approvalReceipt = await basePublicClient.waitForTransactionReceipt({
228
+ hash: approvalHash as `0x${string}`,
229
+ confirmations: 1,
230
+ });
231
+
232
+ if (approvalReceipt?.status !== "success") {
233
+ toast.error("Approval failed. Please try again.");
234
+ return;
235
+ }
236
+ }
237
+
238
+ // Execute the stake
239
+ toast.info("Staking B3...");
240
+
241
+ const stakeData = encodeFunctionData({
242
+ abi: ABI_ERC20_STAKING,
243
+ functionName: "stake",
244
+ args: [BigInt(userStakeAmount), recipientAddress as `0x${string}`],
245
+ });
246
+
247
+ const stakeHash = await switchChainAndExecute(base.id, {
248
+ to: ERC20Staking as `0x${string}`,
249
+ data: stakeData,
250
+ value: BigInt(0),
251
+ });
252
+
253
+ if (stakeHash) {
254
+ setStakingTxHash(stakeHash);
255
+ toast.success("Staking transaction submitted!");
256
+ }
257
+ } catch (error) {
258
+ console.error("@@b3-stake-custom-exact-in:error:", error);
259
+ toast.error("Staking failed. Please try again.");
260
+ setShowSuccessModal(false); // Ensure modal doesn't show on error
261
+ } finally {
262
+ setIsStaking(false);
263
+ }
264
+ };
265
+
266
+ const confirmAmount = () => {
267
+ if (!isAmountValid) {
268
+ toast.error("Please enter a valid amount to stake");
269
+ return;
270
+ }
271
+
272
+ // Check if user has sufficient B3 balance for direct staking
273
+ const hasEnoughBalance = b3RawBalance && BigInt(userStakeAmount) <= b3RawBalance;
274
+
275
+ if (hasEnoughBalance) {
276
+ // User has enough B3, proceed with direct staking
277
+ handleDirectStaking();
278
+ } else {
279
+ // User needs more B3, proceed to AnySpend conversion flow
280
+ setShowAmountPrompt(false);
281
+ }
282
+ };
283
+
284
+ const header = () => (
285
+ <>
286
+ <div className="relative mx-auto size-32">
287
+ <img alt="b3 coin" className="size-full" src="https://cdn.b3.fun/b3-coin-3d.png" />
288
+ </div>
289
+ <div className="from-b3-react-background to-as-on-surface-1 mt-[-60px] w-full rounded-t-lg bg-gradient-to-t">
290
+ <div className="h-[60px] w-full" />
291
+ <div className="mb-1 flex w-full flex-col items-center gap-2 p-5">
292
+ <span className="font-sf-rounded text-2xl font-semibold">
293
+ Swap & Stake {userStakeAmount ? formatTokenAmount(BigInt(userStakeAmount), 18) : ""} B3 (Exact In)
294
+ </span>
295
+ </div>
296
+ </div>
297
+ </>
298
+ );
299
+
300
+ const onFocusStakeAmountInput = () => {
301
+ window.scrollTo(0, 0);
302
+ document.body.scrollTop = 0;
303
+ };
304
+
305
+ const customExactInConfig = {
306
+ functionAbi: STAKE_FUNCTION_ABI,
307
+ functionName: "stake",
308
+ functionArgs: ["{{amount_out}}", normalizeAddress(recipientAddress)],
309
+ to: ERC20Staking,
310
+ spenderAddress: ERC20Staking,
311
+ action: "stake B3",
312
+ };
313
+
314
+ // Render amount input prompt if no stake amount is provided
315
+ if (showAmountPrompt) {
316
+ return (
317
+ <StyleRoot>
318
+ <div className="bg-b3-react-background flex w-full flex-col items-center">
319
+ <div className="w-full px-4">
320
+ <motion.div
321
+ initial={false}
322
+ animate={{
323
+ opacity: hasMounted ? 1 : 0,
324
+ y: hasMounted ? 0 : 20,
325
+ filter: hasMounted ? "blur(0px)" : "blur(10px)",
326
+ }}
327
+ transition={{ duration: 0.3, delay: 0, ease: "easeInOut" }}
328
+ className="relative mx-auto size-48"
329
+ >
330
+ <video autoPlay muted playsInline className="size-full" src="https://cdn.b3.fun/b3-sphere-to-coin.mp4" />
331
+ </motion.div>
332
+ <motion.div
333
+ initial={false}
334
+ animate={{
335
+ opacity: hasMounted ? 1 : 0,
336
+ y: hasMounted ? 0 : 20,
337
+ filter: hasMounted ? "blur(0px)" : "blur(10px)",
338
+ }}
339
+ transition={{ duration: 0.3, delay: 0.1, ease: "easeInOut" }}
340
+ >
341
+ <h2 className="font-sf-rounded font-neue-montreal-medium mb-1 text-center text-2xl font-semibold">
342
+ {(() => {
343
+ const hasEnoughBalance = b3RawBalance && BigInt(debouncedUserStakeAmount || "0") <= b3RawBalance;
344
+ return hasEnoughBalance || !debouncedAmount ? "Stake B3" : "Swap & Stake B3";
345
+ })()}
346
+ </h2>
347
+ </motion.div>
348
+ </div>
349
+
350
+ <motion.div
351
+ initial={false}
352
+ animate={{
353
+ opacity: hasMounted ? 1 : 0,
354
+ y: hasMounted ? 0 : 20,
355
+ filter: hasMounted ? "blur(0px)" : "blur(10px)",
356
+ }}
357
+ transition={{ duration: 0.3, delay: 0.2, ease: "easeInOut" }}
358
+ className="bg-b3-react-background w-full p-6"
359
+ >
360
+ <div className="mb-2">
361
+ <div className="flex items-center justify-between">
362
+ <p className="text-as-primary/70 text-sm font-medium">I want to stake</p>
363
+ <span className="text-as-primary/50 flex items-center gap-1 text-sm">
364
+ Available: {isBalanceLoading ? <Loader2 className="h-3 w-3 animate-spin" /> : `${b3Balance} B3`}
365
+ </span>
366
+ </div>
367
+ </div>
368
+
369
+ <div className="relative">
370
+ <Input
371
+ onFocus={onFocusStakeAmountInput}
372
+ type="text"
373
+ placeholder="0.00"
374
+ value={displayAmount}
375
+ onChange={e => validateAndSetAmount(e.target.value)}
376
+ className={`h-14 px-4 text-lg ${!isAmountValid && displayAmount ? "border-as-red" : "border-b3-react-border"}`}
377
+ />
378
+ <div className="font-pack absolute right-4 top-1/2 -translate-y-1/2 text-lg font-medium text-blue-500/70">
379
+ B3
380
+ </div>
381
+ </div>
382
+
383
+ {!isAmountValid && displayAmount && <p className="text-as-red mt-2 text-sm">{validationError}</p>}
384
+
385
+ <div className="mt-4">
386
+ {(() => {
387
+ const hasEnoughBalance = b3RawBalance && BigInt(debouncedUserStakeAmount || "0") <= b3RawBalance;
388
+
389
+ if (!hasEnoughBalance || !debouncedAmount) {
390
+ return (
391
+ <div className="bg-as-brand/10 flex flex-col items-center gap-2 rounded-lg p-4 pb-5">
392
+ <div className="flex items-center justify-center gap-2">
393
+ <span className="text-as-primary text-sm font-semibold">Swap & stake from any token</span>
394
+ <TextLoop>
395
+ <EthIcon className="h-8 w-8" />
396
+ <SolIcon className="h-8 w-8" />
397
+ <UsdcIcon className="h-8 w-8" />
398
+ </TextLoop>
399
+ <ArrowRight className="text-as-primary h-4 w-4" />
400
+ <img src="https://cdn.b3.fun/b3-coin-3d.png" className="h-7 w-7" alt="B3 Token" />
401
+ </div>
402
+ <p className="text-as-primary/50 text-sm font-medium">
403
+ {debouncedAmount
404
+ ? `No problem, we'll help you swap to ${debouncedAmount} B3!`
405
+ : "Not enough B3? We'll help you swap from other coins."}
406
+ </p>
407
+ </div>
408
+ );
409
+ }
410
+ })()}
411
+ </div>
412
+
413
+ <Button
414
+ onClick={confirmAmount}
415
+ disabled={!isAmountValid || !displayAmount || isStaking || isTxPending || isSwitchingOrExecuting}
416
+ className="bg-as-brand hover:bg-as-brand/90 text-as-primary mt-4 h-14 w-full rounded-xl text-lg font-medium"
417
+ >
418
+ {isStaking || isSwitchingOrExecuting ? "Staking..." : isTxPending ? "Confirming..." : "Continue"}
419
+ </Button>
420
+ </motion.div>
421
+ </div>
422
+ </StyleRoot>
423
+ );
424
+ }
425
+
426
+ // Success Modal for Direct Staking
427
+ if (showSuccessModal) {
428
+ return (
429
+ <StyleRoot>
430
+ <div className="bg-b3-react-background flex w-full flex-col items-center">
431
+ <div className="w-full p-4">
432
+ <motion.div
433
+ initial={false}
434
+ animate={{
435
+ opacity: hasMounted ? 1 : 0,
436
+ y: hasMounted ? 0 : 20,
437
+ filter: hasMounted ? "blur(0px)" : "blur(10px)",
438
+ }}
439
+ transition={{ duration: 0.3, delay: 0, ease: "easeInOut" }}
440
+ className="relative mx-auto mb-4 size-[120px]"
441
+ >
442
+ <div className="absolute inset-0 scale-95 rounded-[50%] bg-black/30 blur-md"></div>
443
+ <GlareCardRounded className="overflow-hidden rounded-full border-none">
444
+ <img
445
+ alt="b3 coin"
446
+ loading="lazy"
447
+ width="64"
448
+ height="64"
449
+ decoding="async"
450
+ data-nimg="1"
451
+ className="size-full shrink-0 bg-transparent text-transparent"
452
+ src="https://cdn.b3.fun/b3-coin-3d.png"
453
+ />
454
+ <div className="absolute inset-0 rounded-[50%] border border-white/10"></div>
455
+ </GlareCardRounded>
456
+ </motion.div>
457
+ <motion.div
458
+ initial={false}
459
+ animate={{
460
+ opacity: hasMounted ? 1 : 0,
461
+ y: hasMounted ? 0 : 20,
462
+ filter: hasMounted ? "blur(0px)" : "blur(10px)",
463
+ }}
464
+ transition={{ duration: 0.3, delay: 0.1, ease: "easeInOut" }}
465
+ >
466
+ <h2 className="font-sf-rounded mb-1 text-center text-2xl font-semibold">
467
+ Staked {formatTokenAmount(BigInt(userStakeAmount), 18)} B3
468
+ </h2>
469
+ </motion.div>
470
+ </div>
471
+
472
+ <motion.div
473
+ initial={false}
474
+ animate={{
475
+ opacity: hasMounted ? 1 : 0,
476
+ y: hasMounted ? 0 : 20,
477
+ filter: hasMounted ? "blur(0px)" : "blur(10px)",
478
+ }}
479
+ transition={{ duration: 0.3, delay: 0.2, ease: "easeInOut" }}
480
+ className="bg-b3-react-background w-full p-6"
481
+ >
482
+ <div className="mb-6">
483
+ <a
484
+ href={`https://basescan.org/tx/${stakingTxHash}`}
485
+ target="_blank"
486
+ rel="noopener noreferrer"
487
+ className="text-as-primary/70 hover:text-as-primary block break-all text-center font-mono text-sm underline transition-colors"
488
+ >
489
+ View transaction
490
+ </a>
491
+ </div>
492
+
493
+ <Button
494
+ onClick={() => {
495
+ setB3ModalOpen(false);
496
+ onSuccess?.(formatTokenAmount(BigInt(userStakeAmount), 18) ?? "");
497
+ }}
498
+ className="bg-as-brand hover:bg-as-brand/90 text-as-primary h-14 w-full rounded-xl text-lg font-medium"
499
+ >
500
+ Done
501
+ </Button>
502
+ </motion.div>
503
+ </div>
504
+ </StyleRoot>
505
+ );
506
+ }
507
+
508
+ return (
509
+ <AnySpendCustomExactIn
510
+ loadOrder={loadOrder}
511
+ mode={mode}
512
+ recipientAddress={recipientAddress}
513
+ sourceTokenAddress={sourceTokenAddress}
514
+ sourceTokenChainId={sourceTokenChainId}
515
+ destinationToken={B3_TOKEN}
516
+ destinationChainId={base.id}
517
+ customExactInConfig={customExactInConfig}
518
+ header={header}
519
+ onSuccess={onSuccess}
520
+ />
521
+ );
522
+ }
@@ -0,0 +1,73 @@
1
+ import { components } from "@b3dotfun/sdk/anyspend/types/api";
2
+ import { normalizeAddress } from "@b3dotfun/sdk/anyspend/utils";
3
+ import { base } from "viem/chains";
4
+ import { AnySpendCustomExactIn } from "./AnySpendCustomExactIn";
5
+
6
+ const STAKE_FOR_FUNCTION_ABI = JSON.stringify([
7
+ {
8
+ name: "stakeFor",
9
+ type: "function",
10
+ stateMutability: "nonpayable",
11
+ inputs: [
12
+ { name: "user", type: "address" },
13
+ { name: "amount", type: "uint256" },
14
+ ],
15
+ outputs: [],
16
+ },
17
+ ]);
18
+
19
+ export function AnySpendStakeUpsideExactIn({
20
+ loadOrder,
21
+ mode = "modal",
22
+ recipientAddress,
23
+ sourceTokenAddress,
24
+ sourceTokenChainId,
25
+ stakingContractAddress,
26
+ token,
27
+ onSuccess,
28
+ }: {
29
+ loadOrder?: string;
30
+ mode?: "modal" | "page";
31
+ recipientAddress: string;
32
+ sourceTokenAddress?: string;
33
+ sourceTokenChainId?: number;
34
+ stakingContractAddress: string;
35
+ token: components["schemas"]["Token"];
36
+ onSuccess?: (amount: string) => void;
37
+ }) {
38
+ if (!recipientAddress) return null;
39
+
40
+ const header = () => (
41
+ <>
42
+ <div className="from-b3-react-background to-as-on-surface-1 w-full rounded-t-lg bg-gradient-to-t">
43
+ <div className="mb-1 flex w-full flex-col items-center gap-2">
44
+ <span className="font-sf-rounded text-2xl font-semibold">Swap & Stake {token.symbol} (Exact In)</span>
45
+ </div>
46
+ </div>
47
+ </>
48
+ );
49
+
50
+ const customExactInConfig = {
51
+ functionAbi: STAKE_FOR_FUNCTION_ABI,
52
+ functionName: "stakeFor",
53
+ functionArgs: [normalizeAddress(recipientAddress), "{{amount_out}}"],
54
+ to: stakingContractAddress,
55
+ spenderAddress: stakingContractAddress,
56
+ action: `stake ${token.symbol}`,
57
+ };
58
+
59
+ return (
60
+ <AnySpendCustomExactIn
61
+ loadOrder={loadOrder}
62
+ mode={mode}
63
+ recipientAddress={recipientAddress}
64
+ sourceTokenAddress={sourceTokenAddress}
65
+ sourceTokenChainId={sourceTokenChainId}
66
+ destinationToken={token}
67
+ destinationChainId={base.id}
68
+ customExactInConfig={customExactInConfig}
69
+ header={header}
70
+ onSuccess={onSuccess}
71
+ />
72
+ );
73
+ }
@@ -6,6 +6,7 @@ import invariant from "invariant";
6
6
  import { motion } from "motion/react";
7
7
  import { useEffect, useMemo, useRef } from "react";
8
8
  import { toast } from "sonner";
9
+ import { useActiveWallet, useSetActiveWallet } from "thirdweb/react";
9
10
  import { base } from "viem/chains";
10
11
  import { PanelView, useAnyspendFlow } from "../hooks/useAnyspendFlow";
11
12
  import { AnySpendFingerprintWrapper, getFingerprintConfig } from "./AnySpendFingerprintWrapper";
@@ -17,11 +18,10 @@ import { FiatPaymentMethod, FiatPaymentMethodComponent } from "./common/FiatPaym
17
18
  import { OrderDetails } from "./common/OrderDetails";
18
19
  import { PointsDetailPanel } from "./common/PointsDetailPanel";
19
20
  import { RecipientSelection } from "./common/RecipientSelection";
20
- import { useActiveWallet, useSetActiveWallet } from "thirdweb/react";
21
21
 
22
22
  import { ArrowDown, Loader2 } from "lucide-react";
23
- import { PanelOnramp } from "./common/PanelOnramp";
24
23
  import { useGlobalWalletState } from "../../utils";
24
+ import { PanelOnramp } from "./common/PanelOnramp";
25
25
 
26
26
  const SLIPPAGE_PERCENT = 3;
27
27
 
@@ -286,9 +286,12 @@ function AnySpendDepositHypeInner({
286
286
  <CryptoReceiveSection
287
287
  isDepositMode={false}
288
288
  isBuyMode={true}
289
- selectedRecipientAddress={recipientAddress}
289
+ selectedRecipientAddress={selectedRecipientAddress}
290
290
  recipientName={recipientName || undefined}
291
291
  onSelectRecipient={() => setActivePanel(PanelView.RECIPIENT_SELECTION)}
292
+ setRecipientAddress={setSelectedRecipientAddress}
293
+ recipientAddressFromProps={recipientAddress}
294
+ globalAddress={globalAddress}
292
295
  dstAmount={dstAmount}
293
296
  dstToken={B3_TOKEN}
294
297
  dstTokenSymbol={HYPE_TOKEN_DETAILS.SYMBOL}
@@ -304,6 +307,7 @@ function AnySpendDepositHypeInner({
304
307
  anyspendQuote={anyspendQuote}
305
308
  onShowPointsDetail={() => setActivePanel(PanelView.POINTS_DETAIL)}
306
309
  onShowFeeDetail={() => setActivePanel(PanelView.FEE_DETAIL)}
310
+ selectedCryptoPaymentMethod={selectedCryptoPaymentMethod}
307
311
  />
308
312
  )}
309
313
  </div>
@@ -1,4 +1,4 @@
1
- import { useAccountWallet, useProfile, useTokenData } from "@b3dotfun/sdk/global-account/react";
1
+ import { useProfile, useTokenData } from "@b3dotfun/sdk/global-account/react";
2
2
  import { formatUsername } from "@b3dotfun/sdk/shared/utils";
3
3
  import { shortenAddress } from "@b3dotfun/sdk/shared/utils/formatAddress";
4
4
  import { formatDisplayNumber } from "@b3dotfun/sdk/shared/utils/number";
@@ -6,6 +6,7 @@ import { ChevronRight, Info } from "lucide-react";
6
6
  import { motion } from "motion/react";
7
7
  import { useEffect, useRef } from "react";
8
8
  import { components } from "../../../types/api";
9
+ import { useConnectedWalletDisplay } from "../../hooks/useConnectedWalletDisplay";
9
10
  import { CryptoPaymentMethodType } from "./CryptoPaymentMethod";
10
11
  import { OrderTokenAmount } from "./OrderTokenAmount";
11
12
  import { TokenBalance } from "./TokenBalance";
@@ -46,14 +47,10 @@ export function CryptoPaySection({
46
47
  onTokenSelect,
47
48
  onShowFeeDetail,
48
49
  }: CryptoPaySectionProps) {
49
- const { connectedSmartWallet, connectedEOAWallet } = useAccountWallet();
50
50
  const { data: srcTokenMetadata } = useTokenData(selectedSrcToken?.chainId, selectedSrcToken?.address);
51
51
 
52
- // Determine which address to use based on payment method
53
- const walletAddress =
54
- selectedCryptoPaymentMethod === CryptoPaymentMethodType.GLOBAL_WALLET
55
- ? connectedSmartWallet?.getAccount()?.address
56
- : connectedEOAWallet?.getAccount()?.address;
52
+ // Use custom hook to determine wallet address based on payment method
53
+ const { walletAddress } = useConnectedWalletDisplay(selectedCryptoPaymentMethod);
57
54
 
58
55
  const { data: profileData } = useProfile({ address: walletAddress });
59
56
  const connectedName = profileData?.displayName;
@@ -137,6 +134,7 @@ export function CryptoPaySection({
137
134
  </div>
138
135
  <OrderTokenAmount
139
136
  address={walletAddress}
137
+ walletAddress={walletAddress}
140
138
  context="from"
141
139
  inputValue={srcAmount}
142
140
  onChangeInput={value => {