@b3dotfun/sdk 0.0.31-alpha.0 → 0.0.31-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.
@@ -57,6 +57,7 @@ function AnySpendDepositHypeInner({ loadOrder, mode = "modal", recipientAddress,
57
57
  sourceTokenAddress,
58
58
  sourceTokenChainId,
59
59
  slippage: SLIPPAGE_PERCENT,
60
+ disableUrlParamManagement: true,
60
61
  });
61
62
  // Button state logic
62
63
  const btnInfo = (0, react_3.useMemo)(() => {
@@ -233,8 +234,7 @@ function AnySpendDepositHypeInner({ loadOrder, mode = "modal", recipientAddress,
233
234
  const orderDetailsView = ((0, jsx_runtime_1.jsx)("div", { className: "mx-auto w-[460px] max-w-full", children: (0, jsx_runtime_1.jsx)("div", { className: "relative flex flex-col gap-4", children: oat && ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(OrderStatus_1.OrderStatus, { order: oat.data.order, selectedCryptoPaymentMethod: selectedCryptoPaymentMethod }), (0, jsx_runtime_1.jsx)(OrderDetails_1.OrderDetails, { mode: mode, order: oat.data.order, depositTxs: oat.data.depositTxs, relayTx: oat.data.relayTx, executeTx: oat.data.executeTx, refundTxs: oat.data.refundTxs, cryptoPaymentMethod: paymentType === "fiat" ? CryptoPaymentMethod_1.CryptoPaymentMethodType.NONE : selectedCryptoPaymentMethod, onBack: () => {
234
235
  setOrderId(undefined);
235
236
  setActivePanel(useAnyspendFlow_1.PanelView.MAIN);
236
- onSuccess?.();
237
- } })] })) }) }));
237
+ }, disableUrlParamManagement: true })] })) }) }));
238
238
  // Loading view
239
239
  const loadingView = ((0, jsx_runtime_1.jsx)("div", { className: "mx-auto flex w-full flex-col items-center gap-4 p-5", children: (0, jsx_runtime_1.jsx)("div", { className: "text-as-primary", children: "Loading order details..." }) }));
240
240
  // Panel views
@@ -9,6 +9,7 @@ interface OrderDetailsProps {
9
9
  refundTxs: components["schemas"]["RefundTx"][] | null;
10
10
  cryptoPaymentMethod?: CryptoPaymentMethodType;
11
11
  onBack?: () => void;
12
+ disableUrlParamManagement?: boolean;
12
13
  }
13
14
  export declare const OrderDetails: import("react").NamedExoticComponent<OrderDetailsProps>;
14
15
  export declare const OrderDetailsLoadingView: import("react/jsx-runtime").JSX.Element;
@@ -130,7 +130,7 @@ function roundTokenAmount(amount) {
130
130
  const roundedDecimalPart = digits.join("");
131
131
  return `${wholePart}.${roundedDecimalPart}`;
132
132
  }
133
- exports.OrderDetails = (0, react_5.memo)(function OrderDetails({ mode = "modal", order, depositTxs, relayTx, executeTx, refundTxs, cryptoPaymentMethod, onBack, }) {
133
+ exports.OrderDetails = (0, react_5.memo)(function OrderDetails({ mode = "modal", order, depositTxs, relayTx, executeTx, refundTxs, cryptoPaymentMethod, onBack, disableUrlParamManagement = false, }) {
134
134
  const router = (0, hooks_1.useRouter)();
135
135
  const searchParams = (0, hooks_1.useSearchParams)();
136
136
  // Read crypto payment method from URL parameters
@@ -196,12 +196,16 @@ exports.OrderDetails = (0, react_5.memo)(function OrderDetails({ mode = "modal",
196
196
  };
197
197
  // When waitingForDeposit is true, we show a message to the user to wait for the deposit to be processed.
198
198
  const setWaitingForDeposit = (0, react_5.useCallback)(() => {
199
+ if (disableUrlParamManagement)
200
+ return;
199
201
  const params = new URLSearchParams(searchParams.toString());
200
202
  params.set("waitingForDeposit", "true");
201
203
  router.push(`?${params}`);
202
- }, [router, searchParams]);
204
+ }, [router, searchParams, disableUrlParamManagement]);
203
205
  // Clean up URL parameters before closing modal or navigating back
204
206
  const cleanupUrlParams = (0, react_5.useCallback)(() => {
207
+ if (disableUrlParamManagement)
208
+ return;
205
209
  const params = new URLSearchParams(searchParams.toString());
206
210
  params.delete("waitingForDeposit");
207
211
  params.delete("orderId");
@@ -210,7 +214,7 @@ exports.OrderDetails = (0, react_5.memo)(function OrderDetails({ mode = "modal",
210
214
  if (params.toString() !== searchParams.toString()) {
211
215
  router.push(`?${params}`);
212
216
  }
213
- }, [router, searchParams]);
217
+ }, [router, searchParams, disableUrlParamManagement]);
214
218
  // Helper functions that clean up URL params before executing actions
215
219
  const handleCloseModal = (0, react_5.useCallback)(() => {
216
220
  cleanupUrlParams();
@@ -19,8 +19,9 @@ interface UseAnyspendFlowProps {
19
19
  sourceTokenAddress?: string;
20
20
  sourceTokenChainId?: number;
21
21
  slippage?: number;
22
+ disableUrlParamManagement?: boolean;
22
23
  }
23
- export declare function useAnyspendFlow({ paymentType, recipientAddress, loadOrder, isDepositMode, onOrderSuccess, onTransactionSuccess, sourceTokenAddress, sourceTokenChainId, slippage, }: UseAnyspendFlowProps): {
24
+ export declare function useAnyspendFlow({ paymentType, recipientAddress, loadOrder, isDepositMode, onOrderSuccess, onTransactionSuccess, sourceTokenAddress, sourceTokenChainId, slippage, disableUrlParamManagement, }: UseAnyspendFlowProps): {
24
25
  activePanel: PanelView;
25
26
  setActivePanel: import("react").Dispatch<import("react").SetStateAction<PanelView>>;
26
27
  orderId: string | undefined;
@@ -75,6 +76,8 @@ export declare function useAnyspendFlow({ paymentType, recipientAddress, loadOrd
75
76
  setSelectedRecipientAddress: import("react").Dispatch<import("react").SetStateAction<string | undefined>>;
76
77
  recipientName: string | null | undefined;
77
78
  globalAddress: string | undefined;
79
+ hasEnoughBalance: boolean;
80
+ isBalanceLoading: boolean;
78
81
  anyspendQuote: {
79
82
  success: boolean;
80
83
  message: string;
@@ -11,6 +11,7 @@ const react_3 = require("react");
11
11
  const sonner_1 = require("sonner");
12
12
  const viem_1 = require("viem");
13
13
  const chains_1 = require("viem/chains");
14
+ const wagmi_1 = require("wagmi");
14
15
  const CryptoPaymentMethod_1 = require("../components/common/CryptoPaymentMethod");
15
16
  const FiatPaymentMethod_1 = require("../components/common/FiatPaymentMethod");
16
17
  var PanelView;
@@ -22,7 +23,7 @@ var PanelView;
22
23
  PanelView[PanelView["ORDER_DETAILS"] = 4] = "ORDER_DETAILS";
23
24
  PanelView[PanelView["LOADING"] = 5] = "LOADING";
24
25
  })(PanelView || (exports.PanelView = PanelView = {}));
25
- function useAnyspendFlow({ paymentType = "crypto", recipientAddress, loadOrder, isDepositMode = false, onOrderSuccess, onTransactionSuccess, sourceTokenAddress, sourceTokenChainId, slippage = 0, }) {
26
+ function useAnyspendFlow({ paymentType = "crypto", recipientAddress, loadOrder, isDepositMode = false, onOrderSuccess, onTransactionSuccess, sourceTokenAddress, sourceTokenChainId, slippage = 0, disableUrlParamManagement = false, }) {
26
27
  const searchParams = (0, react_2.useSearchParamsSSR)();
27
28
  const router = (0, react_2.useRouter)();
28
29
  // Panel and order state
@@ -42,6 +43,7 @@ function useAnyspendFlow({ paymentType = "crypto", recipientAddress, loadOrder,
42
43
  const [selectedFiatPaymentMethod, setSelectedFiatPaymentMethod] = (0, react_3.useState)(FiatPaymentMethod_1.FiatPaymentMethod.NONE);
43
44
  // Recipient state
44
45
  const { address: globalAddress } = (0, react_2.useAccountWallet)();
46
+ const { address: wagmiAddress } = (0, wagmi_1.useAccount)();
45
47
  const [selectedRecipientAddress, setSelectedRecipientAddress] = (0, react_3.useState)(recipientAddress);
46
48
  const recipientProfile = (0, react_2.useProfile)({ address: selectedRecipientAddress, fresh: true });
47
49
  const recipientName = recipientProfile.data?.name;
@@ -51,11 +53,34 @@ function useAnyspendFlow({ paymentType = "crypto", recipientAddress, loadOrder,
51
53
  setSelectedRecipientAddress(globalAddress);
52
54
  }
53
55
  }, [selectedRecipientAddress, globalAddress]);
56
+ // Check token balance for crypto payments
57
+ const { rawBalance, isLoading: isBalanceLoading } = (0, react_2.useTokenBalance)({
58
+ token: selectedSrcToken,
59
+ address: wagmiAddress,
60
+ });
61
+ // Check if user has enough balance
62
+ const hasEnoughBalance = (0, react_3.useMemo)(() => {
63
+ if (!rawBalance || isBalanceLoading || paymentType !== "crypto")
64
+ return false;
65
+ try {
66
+ const requiredAmount = (0, viem_1.parseUnits)(srcAmount.replace(/,/g, ""), selectedSrcToken.decimals);
67
+ return rawBalance >= requiredAmount;
68
+ }
69
+ catch {
70
+ return false;
71
+ }
72
+ }, [rawBalance, srcAmount, selectedSrcToken.decimals, isBalanceLoading, paymentType]);
73
+ // Auto-set crypto payment method based on balance
54
74
  (0, react_3.useEffect)(() => {
55
- if (paymentType === "crypto") {
56
- setSelectedCryptoPaymentMethod(CryptoPaymentMethod_1.CryptoPaymentMethodType.CONNECT_WALLET);
75
+ if (paymentType === "crypto" && !isBalanceLoading) {
76
+ if (hasEnoughBalance) {
77
+ setSelectedCryptoPaymentMethod(CryptoPaymentMethod_1.CryptoPaymentMethodType.CONNECT_WALLET);
78
+ }
79
+ else {
80
+ setSelectedCryptoPaymentMethod(CryptoPaymentMethod_1.CryptoPaymentMethodType.TRANSFER_CRYPTO);
81
+ }
57
82
  }
58
- }, [paymentType]);
83
+ }, [paymentType, hasEnoughBalance, isBalanceLoading]);
59
84
  // Fetch specific token when sourceTokenAddress and sourceTokenChainId are provided
60
85
  (0, react_3.useEffect)(() => {
61
86
  const fetchSourceToken = async () => {
@@ -114,14 +139,14 @@ function useAnyspendFlow({ paymentType = "crypto", recipientAddress, loadOrder,
114
139
  }, [anyspendQuote, slippage]);
115
140
  // Update useEffect for URL parameter to not override loadOrder
116
141
  (0, react_3.useEffect)(() => {
117
- if (loadOrder)
118
- return; // Skip if we have a loadOrder
142
+ if (loadOrder || disableUrlParamManagement)
143
+ return; // Skip if we have a loadOrder or URL param management is disabled
119
144
  const orderIdParam = searchParams.get("orderId");
120
145
  if (orderIdParam) {
121
146
  setOrderId(orderIdParam);
122
147
  setActivePanel(PanelView.ORDER_DETAILS);
123
148
  }
124
- }, [searchParams, loadOrder]);
149
+ }, [searchParams, loadOrder, disableUrlParamManagement]);
125
150
  // Order creation hooks
126
151
  const { createOrder, isCreatingOrder } = (0, react_1.useAnyspendCreateOrder)({
127
152
  onSuccess: data => {
@@ -130,17 +155,19 @@ function useAnyspendFlow({ paymentType = "crypto", recipientAddress, loadOrder,
130
155
  setActivePanel(PanelView.ORDER_DETAILS);
131
156
  onOrderSuccess?.(newOrderId);
132
157
  // Add orderId and payment method to URL for persistence
133
- const params = new URLSearchParams(searchParams.toString()); // Preserve existing params
134
- params.set("orderId", newOrderId);
135
- if (selectedCryptoPaymentMethod !== CryptoPaymentMethod_1.CryptoPaymentMethodType.NONE) {
136
- console.log("Setting cryptoPaymentMethod in URL:", selectedCryptoPaymentMethod);
137
- params.set("cryptoPaymentMethod", selectedCryptoPaymentMethod);
138
- }
139
- else {
140
- console.log("Payment method is NONE, not setting in URL");
158
+ if (!disableUrlParamManagement) {
159
+ const params = new URLSearchParams(searchParams.toString()); // Preserve existing params
160
+ params.set("orderId", newOrderId);
161
+ if (selectedCryptoPaymentMethod !== CryptoPaymentMethod_1.CryptoPaymentMethodType.NONE) {
162
+ console.log("Setting cryptoPaymentMethod in URL:", selectedCryptoPaymentMethod);
163
+ params.set("cryptoPaymentMethod", selectedCryptoPaymentMethod);
164
+ }
165
+ else {
166
+ console.log("Payment method is NONE, not setting in URL");
167
+ }
168
+ console.log("Final URL params:", params.toString());
169
+ router.push(`${window.location.pathname}?${params.toString()}`);
141
170
  }
142
- console.log("Final URL params:", params.toString());
143
- router.push(`${window.location.pathname}?${params.toString()}`);
144
171
  },
145
172
  onError: error => {
146
173
  console.error(error);
@@ -196,6 +223,9 @@ function useAnyspendFlow({ paymentType = "crypto", recipientAddress, loadOrder,
196
223
  setSelectedRecipientAddress,
197
224
  recipientName,
198
225
  globalAddress,
226
+ // Balance check
227
+ hasEnoughBalance,
228
+ isBalanceLoading,
199
229
  // Quote data
200
230
  anyspendQuote,
201
231
  isLoadingAnyspendQuote,
@@ -50,6 +50,7 @@ function AnySpendDepositHypeInner({ loadOrder, mode = "modal", recipientAddress,
50
50
  sourceTokenAddress,
51
51
  sourceTokenChainId,
52
52
  slippage: SLIPPAGE_PERCENT,
53
+ disableUrlParamManagement: true,
53
54
  });
54
55
  // Button state logic
55
56
  const btnInfo = useMemo(() => {
@@ -226,8 +227,7 @@ function AnySpendDepositHypeInner({ loadOrder, mode = "modal", recipientAddress,
226
227
  const orderDetailsView = (_jsx("div", { className: "mx-auto w-[460px] max-w-full", children: _jsx("div", { className: "relative flex flex-col gap-4", children: oat && (_jsxs(_Fragment, { children: [_jsx(OrderStatus, { order: oat.data.order, selectedCryptoPaymentMethod: selectedCryptoPaymentMethod }), _jsx(OrderDetails, { mode: mode, order: oat.data.order, depositTxs: oat.data.depositTxs, relayTx: oat.data.relayTx, executeTx: oat.data.executeTx, refundTxs: oat.data.refundTxs, cryptoPaymentMethod: paymentType === "fiat" ? CryptoPaymentMethodType.NONE : selectedCryptoPaymentMethod, onBack: () => {
227
228
  setOrderId(undefined);
228
229
  setActivePanel(PanelView.MAIN);
229
- onSuccess?.();
230
- } })] })) }) }));
230
+ }, disableUrlParamManagement: true })] })) }) }));
231
231
  // Loading view
232
232
  const loadingView = (_jsx("div", { className: "mx-auto flex w-full flex-col items-center gap-4 p-5", children: _jsx("div", { className: "text-as-primary", children: "Loading order details..." }) }));
233
233
  // Panel views
@@ -9,6 +9,7 @@ interface OrderDetailsProps {
9
9
  refundTxs: components["schemas"]["RefundTx"][] | null;
10
10
  cryptoPaymentMethod?: CryptoPaymentMethodType;
11
11
  onBack?: () => void;
12
+ disableUrlParamManagement?: boolean;
12
13
  }
13
14
  export declare const OrderDetails: import("react").NamedExoticComponent<OrderDetailsProps>;
14
15
  export declare const OrderDetailsLoadingView: import("react/jsx-runtime").JSX.Element;
@@ -124,7 +124,7 @@ function roundTokenAmount(amount) {
124
124
  const roundedDecimalPart = digits.join("");
125
125
  return `${wholePart}.${roundedDecimalPart}`;
126
126
  }
127
- export const OrderDetails = memo(function OrderDetails({ mode = "modal", order, depositTxs, relayTx, executeTx, refundTxs, cryptoPaymentMethod, onBack, }) {
127
+ export const OrderDetails = memo(function OrderDetails({ mode = "modal", order, depositTxs, relayTx, executeTx, refundTxs, cryptoPaymentMethod, onBack, disableUrlParamManagement = false, }) {
128
128
  const router = useRouter();
129
129
  const searchParams = useSearchParams();
130
130
  // Read crypto payment method from URL parameters
@@ -190,12 +190,16 @@ export const OrderDetails = memo(function OrderDetails({ mode = "modal", order,
190
190
  };
191
191
  // When waitingForDeposit is true, we show a message to the user to wait for the deposit to be processed.
192
192
  const setWaitingForDeposit = useCallback(() => {
193
+ if (disableUrlParamManagement)
194
+ return;
193
195
  const params = new URLSearchParams(searchParams.toString());
194
196
  params.set("waitingForDeposit", "true");
195
197
  router.push(`?${params}`);
196
- }, [router, searchParams]);
198
+ }, [router, searchParams, disableUrlParamManagement]);
197
199
  // Clean up URL parameters before closing modal or navigating back
198
200
  const cleanupUrlParams = useCallback(() => {
201
+ if (disableUrlParamManagement)
202
+ return;
199
203
  const params = new URLSearchParams(searchParams.toString());
200
204
  params.delete("waitingForDeposit");
201
205
  params.delete("orderId");
@@ -204,7 +208,7 @@ export const OrderDetails = memo(function OrderDetails({ mode = "modal", order,
204
208
  if (params.toString() !== searchParams.toString()) {
205
209
  router.push(`?${params}`);
206
210
  }
207
- }, [router, searchParams]);
211
+ }, [router, searchParams, disableUrlParamManagement]);
208
212
  // Helper functions that clean up URL params before executing actions
209
213
  const handleCloseModal = useCallback(() => {
210
214
  cleanupUrlParams();
@@ -19,8 +19,9 @@ interface UseAnyspendFlowProps {
19
19
  sourceTokenAddress?: string;
20
20
  sourceTokenChainId?: number;
21
21
  slippage?: number;
22
+ disableUrlParamManagement?: boolean;
22
23
  }
23
- export declare function useAnyspendFlow({ paymentType, recipientAddress, loadOrder, isDepositMode, onOrderSuccess, onTransactionSuccess, sourceTokenAddress, sourceTokenChainId, slippage, }: UseAnyspendFlowProps): {
24
+ export declare function useAnyspendFlow({ paymentType, recipientAddress, loadOrder, isDepositMode, onOrderSuccess, onTransactionSuccess, sourceTokenAddress, sourceTokenChainId, slippage, disableUrlParamManagement, }: UseAnyspendFlowProps): {
24
25
  activePanel: PanelView;
25
26
  setActivePanel: import("react").Dispatch<import("react").SetStateAction<PanelView>>;
26
27
  orderId: string | undefined;
@@ -75,6 +76,8 @@ export declare function useAnyspendFlow({ paymentType, recipientAddress, loadOrd
75
76
  setSelectedRecipientAddress: import("react").Dispatch<import("react").SetStateAction<string | undefined>>;
76
77
  recipientName: string | null | undefined;
77
78
  globalAddress: string | undefined;
79
+ hasEnoughBalance: boolean;
80
+ isBalanceLoading: boolean;
78
81
  anyspendQuote: {
79
82
  success: boolean;
80
83
  message: string;
@@ -1,12 +1,13 @@
1
1
  import { B3_TOKEN, getDefaultToken, USDC_BASE } from "../../../anyspend/index.js";
2
2
  import { useAnyspendCreateOnrampOrder, useAnyspendCreateOrder, useAnyspendOrderAndTransactions, useAnyspendQuote, useGeoOnrampOptions, } from "../../../anyspend/react/index.js";
3
3
  import { anyspendService } from "../../../anyspend/services/anyspend.js";
4
- import { useAccountWallet, useProfile, useRouter, useSearchParamsSSR } from "../../../global-account/react/index.js";
4
+ import { useAccountWallet, useProfile, useRouter, useSearchParamsSSR, useTokenBalance, } from "../../../global-account/react/index.js";
5
5
  import { formatTokenAmount, formatUnits } from "../../../shared/utils/number.js";
6
- import { useEffect, useState } from "react";
6
+ import { useEffect, useMemo, useState } from "react";
7
7
  import { toast } from "sonner";
8
8
  import { parseUnits } from "viem";
9
9
  import { base, mainnet } from "viem/chains";
10
+ import { useAccount } from "wagmi";
10
11
  import { CryptoPaymentMethodType } from "../components/common/CryptoPaymentMethod.js";
11
12
  import { FiatPaymentMethod } from "../components/common/FiatPaymentMethod.js";
12
13
  export var PanelView;
@@ -18,7 +19,7 @@ export var PanelView;
18
19
  PanelView[PanelView["ORDER_DETAILS"] = 4] = "ORDER_DETAILS";
19
20
  PanelView[PanelView["LOADING"] = 5] = "LOADING";
20
21
  })(PanelView || (PanelView = {}));
21
- export function useAnyspendFlow({ paymentType = "crypto", recipientAddress, loadOrder, isDepositMode = false, onOrderSuccess, onTransactionSuccess, sourceTokenAddress, sourceTokenChainId, slippage = 0, }) {
22
+ export function useAnyspendFlow({ paymentType = "crypto", recipientAddress, loadOrder, isDepositMode = false, onOrderSuccess, onTransactionSuccess, sourceTokenAddress, sourceTokenChainId, slippage = 0, disableUrlParamManagement = false, }) {
22
23
  const searchParams = useSearchParamsSSR();
23
24
  const router = useRouter();
24
25
  // Panel and order state
@@ -38,6 +39,7 @@ export function useAnyspendFlow({ paymentType = "crypto", recipientAddress, load
38
39
  const [selectedFiatPaymentMethod, setSelectedFiatPaymentMethod] = useState(FiatPaymentMethod.NONE);
39
40
  // Recipient state
40
41
  const { address: globalAddress } = useAccountWallet();
42
+ const { address: wagmiAddress } = useAccount();
41
43
  const [selectedRecipientAddress, setSelectedRecipientAddress] = useState(recipientAddress);
42
44
  const recipientProfile = useProfile({ address: selectedRecipientAddress, fresh: true });
43
45
  const recipientName = recipientProfile.data?.name;
@@ -47,11 +49,34 @@ export function useAnyspendFlow({ paymentType = "crypto", recipientAddress, load
47
49
  setSelectedRecipientAddress(globalAddress);
48
50
  }
49
51
  }, [selectedRecipientAddress, globalAddress]);
52
+ // Check token balance for crypto payments
53
+ const { rawBalance, isLoading: isBalanceLoading } = useTokenBalance({
54
+ token: selectedSrcToken,
55
+ address: wagmiAddress,
56
+ });
57
+ // Check if user has enough balance
58
+ const hasEnoughBalance = useMemo(() => {
59
+ if (!rawBalance || isBalanceLoading || paymentType !== "crypto")
60
+ return false;
61
+ try {
62
+ const requiredAmount = parseUnits(srcAmount.replace(/,/g, ""), selectedSrcToken.decimals);
63
+ return rawBalance >= requiredAmount;
64
+ }
65
+ catch {
66
+ return false;
67
+ }
68
+ }, [rawBalance, srcAmount, selectedSrcToken.decimals, isBalanceLoading, paymentType]);
69
+ // Auto-set crypto payment method based on balance
50
70
  useEffect(() => {
51
- if (paymentType === "crypto") {
52
- setSelectedCryptoPaymentMethod(CryptoPaymentMethodType.CONNECT_WALLET);
71
+ if (paymentType === "crypto" && !isBalanceLoading) {
72
+ if (hasEnoughBalance) {
73
+ setSelectedCryptoPaymentMethod(CryptoPaymentMethodType.CONNECT_WALLET);
74
+ }
75
+ else {
76
+ setSelectedCryptoPaymentMethod(CryptoPaymentMethodType.TRANSFER_CRYPTO);
77
+ }
53
78
  }
54
- }, [paymentType]);
79
+ }, [paymentType, hasEnoughBalance, isBalanceLoading]);
55
80
  // Fetch specific token when sourceTokenAddress and sourceTokenChainId are provided
56
81
  useEffect(() => {
57
82
  const fetchSourceToken = async () => {
@@ -110,14 +135,14 @@ export function useAnyspendFlow({ paymentType = "crypto", recipientAddress, load
110
135
  }, [anyspendQuote, slippage]);
111
136
  // Update useEffect for URL parameter to not override loadOrder
112
137
  useEffect(() => {
113
- if (loadOrder)
114
- return; // Skip if we have a loadOrder
138
+ if (loadOrder || disableUrlParamManagement)
139
+ return; // Skip if we have a loadOrder or URL param management is disabled
115
140
  const orderIdParam = searchParams.get("orderId");
116
141
  if (orderIdParam) {
117
142
  setOrderId(orderIdParam);
118
143
  setActivePanel(PanelView.ORDER_DETAILS);
119
144
  }
120
- }, [searchParams, loadOrder]);
145
+ }, [searchParams, loadOrder, disableUrlParamManagement]);
121
146
  // Order creation hooks
122
147
  const { createOrder, isCreatingOrder } = useAnyspendCreateOrder({
123
148
  onSuccess: data => {
@@ -126,17 +151,19 @@ export function useAnyspendFlow({ paymentType = "crypto", recipientAddress, load
126
151
  setActivePanel(PanelView.ORDER_DETAILS);
127
152
  onOrderSuccess?.(newOrderId);
128
153
  // Add orderId and payment method to URL for persistence
129
- const params = new URLSearchParams(searchParams.toString()); // Preserve existing params
130
- params.set("orderId", newOrderId);
131
- if (selectedCryptoPaymentMethod !== CryptoPaymentMethodType.NONE) {
132
- console.log("Setting cryptoPaymentMethod in URL:", selectedCryptoPaymentMethod);
133
- params.set("cryptoPaymentMethod", selectedCryptoPaymentMethod);
134
- }
135
- else {
136
- console.log("Payment method is NONE, not setting in URL");
154
+ if (!disableUrlParamManagement) {
155
+ const params = new URLSearchParams(searchParams.toString()); // Preserve existing params
156
+ params.set("orderId", newOrderId);
157
+ if (selectedCryptoPaymentMethod !== CryptoPaymentMethodType.NONE) {
158
+ console.log("Setting cryptoPaymentMethod in URL:", selectedCryptoPaymentMethod);
159
+ params.set("cryptoPaymentMethod", selectedCryptoPaymentMethod);
160
+ }
161
+ else {
162
+ console.log("Payment method is NONE, not setting in URL");
163
+ }
164
+ console.log("Final URL params:", params.toString());
165
+ router.push(`${window.location.pathname}?${params.toString()}`);
137
166
  }
138
- console.log("Final URL params:", params.toString());
139
- router.push(`${window.location.pathname}?${params.toString()}`);
140
167
  },
141
168
  onError: error => {
142
169
  console.error(error);
@@ -192,6 +219,9 @@ export function useAnyspendFlow({ paymentType = "crypto", recipientAddress, load
192
219
  setSelectedRecipientAddress,
193
220
  recipientName,
194
221
  globalAddress,
222
+ // Balance check
223
+ hasEnoughBalance,
224
+ isBalanceLoading,
195
225
  // Quote data
196
226
  anyspendQuote,
197
227
  isLoadingAnyspendQuote,
@@ -9,6 +9,7 @@ interface OrderDetailsProps {
9
9
  refundTxs: components["schemas"]["RefundTx"][] | null;
10
10
  cryptoPaymentMethod?: CryptoPaymentMethodType;
11
11
  onBack?: () => void;
12
+ disableUrlParamManagement?: boolean;
12
13
  }
13
14
  export declare const OrderDetails: import("react").NamedExoticComponent<OrderDetailsProps>;
14
15
  export declare const OrderDetailsLoadingView: import("react/jsx-runtime").JSX.Element;
@@ -19,8 +19,9 @@ interface UseAnyspendFlowProps {
19
19
  sourceTokenAddress?: string;
20
20
  sourceTokenChainId?: number;
21
21
  slippage?: number;
22
+ disableUrlParamManagement?: boolean;
22
23
  }
23
- export declare function useAnyspendFlow({ paymentType, recipientAddress, loadOrder, isDepositMode, onOrderSuccess, onTransactionSuccess, sourceTokenAddress, sourceTokenChainId, slippage, }: UseAnyspendFlowProps): {
24
+ export declare function useAnyspendFlow({ paymentType, recipientAddress, loadOrder, isDepositMode, onOrderSuccess, onTransactionSuccess, sourceTokenAddress, sourceTokenChainId, slippage, disableUrlParamManagement, }: UseAnyspendFlowProps): {
24
25
  activePanel: PanelView;
25
26
  setActivePanel: import("react").Dispatch<import("react").SetStateAction<PanelView>>;
26
27
  orderId: string | undefined;
@@ -75,6 +76,8 @@ export declare function useAnyspendFlow({ paymentType, recipientAddress, loadOrd
75
76
  setSelectedRecipientAddress: import("react").Dispatch<import("react").SetStateAction<string | undefined>>;
76
77
  recipientName: string | null | undefined;
77
78
  globalAddress: string | undefined;
79
+ hasEnoughBalance: boolean;
80
+ isBalanceLoading: boolean;
78
81
  anyspendQuote: {
79
82
  success: boolean;
80
83
  message: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@b3dotfun/sdk",
3
- "version": "0.0.31-alpha.0",
3
+ "version": "0.0.31-alpha.1",
4
4
  "source": "src/index.ts",
5
5
  "main": "./dist/cjs/index.js",
6
6
  "react-native": "./dist/cjs/index.native.js",
@@ -115,6 +115,7 @@ function AnySpendDepositHypeInner({
115
115
  sourceTokenAddress,
116
116
  sourceTokenChainId,
117
117
  slippage: SLIPPAGE_PERCENT,
118
+ disableUrlParamManagement: true,
118
119
  });
119
120
 
120
121
  // Button state logic
@@ -434,8 +435,8 @@ function AnySpendDepositHypeInner({
434
435
  onBack={() => {
435
436
  setOrderId(undefined);
436
437
  setActivePanel(PanelView.MAIN);
437
- onSuccess?.();
438
438
  }}
439
+ disableUrlParamManagement
439
440
  />
440
441
  </>
441
442
  )}
@@ -63,6 +63,7 @@ interface OrderDetailsProps {
63
63
  refundTxs: components["schemas"]["RefundTx"][] | null;
64
64
  cryptoPaymentMethod?: CryptoPaymentMethodType; // Now optional since we read from URL
65
65
  onBack?: () => void;
66
+ disableUrlParamManagement?: boolean; // When true, will not modify URL parameters
66
67
  }
67
68
 
68
69
  // Add this helper function near the top or just above the component
@@ -202,6 +203,7 @@ export const OrderDetails = memo(function OrderDetails({
202
203
  refundTxs,
203
204
  cryptoPaymentMethod,
204
205
  onBack,
206
+ disableUrlParamManagement = false,
205
207
  }: OrderDetailsProps) {
206
208
  const router = useRouter();
207
209
  const searchParams = useSearchParams();
@@ -284,13 +286,15 @@ export const OrderDetails = memo(function OrderDetails({
284
286
 
285
287
  // When waitingForDeposit is true, we show a message to the user to wait for the deposit to be processed.
286
288
  const setWaitingForDeposit = useCallback(() => {
289
+ if (disableUrlParamManagement) return;
287
290
  const params = new URLSearchParams(searchParams.toString());
288
291
  params.set("waitingForDeposit", "true");
289
292
  router.push(`?${params}`);
290
- }, [router, searchParams]);
293
+ }, [router, searchParams, disableUrlParamManagement]);
291
294
 
292
295
  // Clean up URL parameters before closing modal or navigating back
293
296
  const cleanupUrlParams = useCallback(() => {
297
+ if (disableUrlParamManagement) return;
294
298
  const params = new URLSearchParams(searchParams.toString());
295
299
  params.delete("waitingForDeposit");
296
300
  params.delete("orderId");
@@ -300,7 +304,7 @@ export const OrderDetails = memo(function OrderDetails({
300
304
  if (params.toString() !== searchParams.toString()) {
301
305
  router.push(`?${params}`);
302
306
  }
303
- }, [router, searchParams]);
307
+ }, [router, searchParams, disableUrlParamManagement]);
304
308
 
305
309
  // Helper functions that clean up URL params before executing actions
306
310
  const handleCloseModal = useCallback(() => {
@@ -7,12 +7,19 @@ import {
7
7
  useGeoOnrampOptions,
8
8
  } from "@b3dotfun/sdk/anyspend/react";
9
9
  import { anyspendService } from "@b3dotfun/sdk/anyspend/services/anyspend";
10
- import { useAccountWallet, useProfile, useRouter, useSearchParamsSSR } from "@b3dotfun/sdk/global-account/react";
10
+ import {
11
+ useAccountWallet,
12
+ useProfile,
13
+ useRouter,
14
+ useSearchParamsSSR,
15
+ useTokenBalance,
16
+ } from "@b3dotfun/sdk/global-account/react";
11
17
  import { formatTokenAmount, formatUnits } from "@b3dotfun/sdk/shared/utils/number";
12
- import { useEffect, useState } from "react";
18
+ import { useEffect, useMemo, useState } from "react";
13
19
  import { toast } from "sonner";
14
20
  import { parseUnits } from "viem";
15
21
  import { base, mainnet } from "viem/chains";
22
+ import { useAccount } from "wagmi";
16
23
  import { components } from "../../types/api";
17
24
  import { CryptoPaymentMethodType } from "../components/common/CryptoPaymentMethod";
18
25
  import { FiatPaymentMethod } from "../components/common/FiatPaymentMethod";
@@ -36,6 +43,7 @@ interface UseAnyspendFlowProps {
36
43
  sourceTokenAddress?: string;
37
44
  sourceTokenChainId?: number;
38
45
  slippage?: number;
46
+ disableUrlParamManagement?: boolean;
39
47
  }
40
48
 
41
49
  export function useAnyspendFlow({
@@ -48,6 +56,7 @@ export function useAnyspendFlow({
48
56
  sourceTokenAddress,
49
57
  sourceTokenChainId,
50
58
  slippage = 0,
59
+ disableUrlParamManagement = false,
51
60
  }: UseAnyspendFlowProps) {
52
61
  const searchParams = useSearchParamsSSR();
53
62
  const router = useRouter();
@@ -76,6 +85,7 @@ export function useAnyspendFlow({
76
85
 
77
86
  // Recipient state
78
87
  const { address: globalAddress } = useAccountWallet();
88
+ const { address: wagmiAddress } = useAccount();
79
89
  const [selectedRecipientAddress, setSelectedRecipientAddress] = useState<string | undefined>(recipientAddress);
80
90
  const recipientProfile = useProfile({ address: selectedRecipientAddress, fresh: true });
81
91
  const recipientName = recipientProfile.data?.name;
@@ -87,11 +97,33 @@ export function useAnyspendFlow({
87
97
  }
88
98
  }, [selectedRecipientAddress, globalAddress]);
89
99
 
100
+ // Check token balance for crypto payments
101
+ const { rawBalance, isLoading: isBalanceLoading } = useTokenBalance({
102
+ token: selectedSrcToken,
103
+ address: wagmiAddress,
104
+ });
105
+
106
+ // Check if user has enough balance
107
+ const hasEnoughBalance = useMemo(() => {
108
+ if (!rawBalance || isBalanceLoading || paymentType !== "crypto") return false;
109
+ try {
110
+ const requiredAmount = parseUnits(srcAmount.replace(/,/g, ""), selectedSrcToken.decimals);
111
+ return rawBalance >= requiredAmount;
112
+ } catch {
113
+ return false;
114
+ }
115
+ }, [rawBalance, srcAmount, selectedSrcToken.decimals, isBalanceLoading, paymentType]);
116
+
117
+ // Auto-set crypto payment method based on balance
90
118
  useEffect(() => {
91
- if (paymentType === "crypto") {
92
- setSelectedCryptoPaymentMethod(CryptoPaymentMethodType.CONNECT_WALLET);
119
+ if (paymentType === "crypto" && !isBalanceLoading) {
120
+ if (hasEnoughBalance) {
121
+ setSelectedCryptoPaymentMethod(CryptoPaymentMethodType.CONNECT_WALLET);
122
+ } else {
123
+ setSelectedCryptoPaymentMethod(CryptoPaymentMethodType.TRANSFER_CRYPTO);
124
+ }
93
125
  }
94
- }, [paymentType]);
126
+ }, [paymentType, hasEnoughBalance, isBalanceLoading]);
95
127
 
96
128
  // Fetch specific token when sourceTokenAddress and sourceTokenChainId are provided
97
129
  useEffect(() => {
@@ -159,14 +191,14 @@ export function useAnyspendFlow({
159
191
 
160
192
  // Update useEffect for URL parameter to not override loadOrder
161
193
  useEffect(() => {
162
- if (loadOrder) return; // Skip if we have a loadOrder
194
+ if (loadOrder || disableUrlParamManagement) return; // Skip if we have a loadOrder or URL param management is disabled
163
195
 
164
196
  const orderIdParam = searchParams.get("orderId");
165
197
  if (orderIdParam) {
166
198
  setOrderId(orderIdParam);
167
199
  setActivePanel(PanelView.ORDER_DETAILS);
168
200
  }
169
- }, [searchParams, loadOrder]);
201
+ }, [searchParams, loadOrder, disableUrlParamManagement]);
170
202
 
171
203
  // Order creation hooks
172
204
  const { createOrder, isCreatingOrder } = useAnyspendCreateOrder({
@@ -177,16 +209,18 @@ export function useAnyspendFlow({
177
209
  onOrderSuccess?.(newOrderId);
178
210
 
179
211
  // Add orderId and payment method to URL for persistence
180
- const params = new URLSearchParams(searchParams.toString()); // Preserve existing params
181
- params.set("orderId", newOrderId);
182
- if (selectedCryptoPaymentMethod !== CryptoPaymentMethodType.NONE) {
183
- console.log("Setting cryptoPaymentMethod in URL:", selectedCryptoPaymentMethod);
184
- params.set("cryptoPaymentMethod", selectedCryptoPaymentMethod);
185
- } else {
186
- console.log("Payment method is NONE, not setting in URL");
212
+ if (!disableUrlParamManagement) {
213
+ const params = new URLSearchParams(searchParams.toString()); // Preserve existing params
214
+ params.set("orderId", newOrderId);
215
+ if (selectedCryptoPaymentMethod !== CryptoPaymentMethodType.NONE) {
216
+ console.log("Setting cryptoPaymentMethod in URL:", selectedCryptoPaymentMethod);
217
+ params.set("cryptoPaymentMethod", selectedCryptoPaymentMethod);
218
+ } else {
219
+ console.log("Payment method is NONE, not setting in URL");
220
+ }
221
+ console.log("Final URL params:", params.toString());
222
+ router.push(`${window.location.pathname}?${params.toString()}`);
187
223
  }
188
- console.log("Final URL params:", params.toString());
189
- router.push(`${window.location.pathname}?${params.toString()}`);
190
224
  },
191
225
  onError: error => {
192
226
  console.error(error);
@@ -245,6 +279,9 @@ export function useAnyspendFlow({
245
279
  setSelectedRecipientAddress,
246
280
  recipientName,
247
281
  globalAddress,
282
+ // Balance check
283
+ hasEnoughBalance,
284
+ isBalanceLoading,
248
285
  // Quote data
249
286
  anyspendQuote,
250
287
  isLoadingAnyspendQuote,