@b3dotfun/sdk 0.1.65-alpha.0 → 0.1.65-alpha.2

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 (28) hide show
  1. package/dist/cjs/anyspend/react/components/AnySpend.js +3 -14
  2. package/dist/cjs/anyspend/react/components/AnySpendCustom.js +4 -50
  3. package/dist/cjs/anyspend/react/components/QRDeposit.js +2 -12
  4. package/dist/cjs/anyspend/react/hooks/index.d.ts +1 -0
  5. package/dist/cjs/anyspend/react/hooks/index.js +1 -0
  6. package/dist/cjs/anyspend/react/hooks/useOnOrderSuccess.d.ts +10 -0
  7. package/dist/cjs/anyspend/react/hooks/useOnOrderSuccess.js +27 -0
  8. package/dist/cjs/global-account/react/hooks/useUserQuery.js +10 -0
  9. package/dist/cjs/global-account/react/stores/userStore.js +1 -0
  10. package/dist/esm/anyspend/react/components/AnySpend.js +3 -14
  11. package/dist/esm/anyspend/react/components/AnySpendCustom.js +4 -17
  12. package/dist/esm/anyspend/react/components/QRDeposit.js +2 -12
  13. package/dist/esm/anyspend/react/hooks/index.d.ts +1 -0
  14. package/dist/esm/anyspend/react/hooks/index.js +1 -0
  15. package/dist/esm/anyspend/react/hooks/useOnOrderSuccess.d.ts +10 -0
  16. package/dist/esm/anyspend/react/hooks/useOnOrderSuccess.js +24 -0
  17. package/dist/esm/global-account/react/hooks/useUserQuery.js +11 -1
  18. package/dist/esm/global-account/react/stores/userStore.js +1 -0
  19. package/dist/types/anyspend/react/hooks/index.d.ts +1 -0
  20. package/dist/types/anyspend/react/hooks/useOnOrderSuccess.d.ts +10 -0
  21. package/package.json +1 -1
  22. package/src/anyspend/react/components/AnySpend.tsx +3 -16
  23. package/src/anyspend/react/components/AnySpendCustom.tsx +3 -18
  24. package/src/anyspend/react/components/QRDeposit.tsx +2 -13
  25. package/src/anyspend/react/hooks/index.ts +1 -0
  26. package/src/anyspend/react/hooks/useOnOrderSuccess.ts +36 -0
  27. package/src/global-account/react/hooks/useUserQuery.ts +12 -1
  28. package/src/global-account/react/stores/userStore.ts +1 -0
@@ -25,6 +25,7 @@ const useAutoSelectCryptoPaymentMethod_1 = require("../hooks/useAutoSelectCrypto
25
25
  const useConnectedWalletDisplay_1 = require("../hooks/useConnectedWalletDisplay");
26
26
  const useCryptoPaymentMethodState_1 = require("../hooks/useCryptoPaymentMethodState");
27
27
  const useDirectTransfer_1 = require("../hooks/useDirectTransfer");
28
+ const useOnOrderSuccess_1 = require("../hooks/useOnOrderSuccess");
28
29
  const useRecipientAddressState_1 = require("../hooks/useRecipientAddressState");
29
30
  const AnySpendFingerprintWrapper_1 = require("./AnySpendFingerprintWrapper");
30
31
  const CryptoPaymentMethod_1 = require("./common/CryptoPaymentMethod");
@@ -72,8 +73,6 @@ function AnySpendInner({ sourceChainId, destinationTokenAddress, destinationToke
72
73
  // Add refs to track URL state
73
74
  const initialUrlProcessed = (0, react_4.useRef)(false);
74
75
  const lastUrlUpdate = (0, react_4.useRef)(null);
75
- // Track if onSuccess has been called for the current order
76
- const onSuccessCalled = (0, react_4.useRef)(false);
77
76
  // Track animation direction for TransitionPanel
78
77
  const animationDirection = (0, react_4.useRef)(null);
79
78
  // Track previous panel for proper back navigation
@@ -493,18 +492,8 @@ function AnySpendInner({ sourceChainId, destinationTokenAddress, destinationToke
493
492
  }
494
493
  }
495
494
  }, [anyspendQuote, isSrcInputDirty, destinationTokenAmount]);
496
- (0, react_4.useEffect)(() => {
497
- if (oat?.data?.order.status === "executed" && !onSuccessCalled.current) {
498
- console.log("Calling onSuccess");
499
- const txHash = oat?.data?.executeTx?.txHash;
500
- onSuccess?.(txHash);
501
- onSuccessCalled.current = true;
502
- }
503
- }, [oat?.data?.order.status, oat?.data?.executeTx?.txHash, onSuccess]);
504
- // Reset flag when orderId changes
505
- (0, react_4.useEffect)(() => {
506
- onSuccessCalled.current = false;
507
- }, [orderId]);
495
+ // Call onSuccess when order is executed
496
+ (0, useOnOrderSuccess_1.useOnOrderSuccess)({ orderData: oat, orderId, onSuccess });
508
497
  const { createOrder, isCreatingOrder } = (0, react_1.useAnyspendCreateOrder)({
509
498
  onSuccess: data => {
510
499
  const orderId = data.data.id;
@@ -1,37 +1,4 @@
1
1
  "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
2
  var __importDefault = (this && this.__importDefault) || function (mod) {
36
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
37
4
  };
@@ -50,9 +17,10 @@ const simplehash_1 = require("../../../shared/utils/simplehash");
50
17
  const invariant_1 = __importDefault(require("invariant"));
51
18
  const lucide_react_1 = require("lucide-react");
52
19
  const react_4 = require("motion/react");
53
- const react_5 = __importStar(require("react"));
20
+ const react_5 = require("react");
54
21
  const chains_1 = require("viem/chains");
55
22
  const useCryptoPaymentMethodState_1 = require("../hooks/useCryptoPaymentMethodState");
23
+ const useOnOrderSuccess_1 = require("../hooks/useOnOrderSuccess");
56
24
  const useRecipientAddressState_1 = require("../hooks/useRecipientAddressState");
57
25
  const AnySpendFingerprintWrapper_1 = require("./AnySpendFingerprintWrapper");
58
26
  const CryptoPaymentMethod_1 = require("./common/CryptoPaymentMethod");
@@ -165,8 +133,6 @@ function AnySpendCustomInner({ loadOrder, mode = "modal", activeTab: activeTabPr
165
133
  globalAddress: currentWallet.address,
166
134
  });
167
135
  const [orderId, setOrderId] = (0, react_5.useState)(loadOrder);
168
- // Track if onSuccess has been called for the current order
169
- const onSuccessCalled = react_5.default.useRef(false);
170
136
  const [srcChainId, setSrcChainId] = (0, react_5.useState)(chains_1.base.id);
171
137
  // Get token list for token balance check
172
138
  const chainName = (0, react_5.useMemo)(() => (0, simplehash_1.simpleHashChainToChainName)(srcChainId), [srcChainId]);
@@ -304,20 +270,8 @@ function AnySpendCustomInner({ loadOrder, mode = "modal", activeTab: activeTabPr
304
270
  }, [activeTab, srcFiatAmountForGeoCheck]);
305
271
  // Get geo data and onramp options (use srcFiatAmountForGeoCheck to check availability regardless of activeTab)
306
272
  const { geoData, isOnrampSupported, coinbaseAvailablePaymentMethods, stripeOnrampSupport, stripeWeb2Support } = (0, react_1.useGeoOnrampOptions)(srcFiatAmountForGeoCheck);
307
- (0, react_5.useEffect)(() => {
308
- if (oat?.data?.order.status === "executed" && !onSuccessCalled.current) {
309
- console.log("Calling onSuccess");
310
- const relayTxs = oat?.data?.relayTxs;
311
- const lastRelayTxHash = relayTxs?.[relayTxs.length - 1]?.txHash;
312
- const txHash = oat?.data?.executeTx?.txHash || lastRelayTxHash;
313
- onSuccess?.(txHash);
314
- onSuccessCalled.current = true;
315
- }
316
- }, [oat?.data?.order.status, oat?.data?.executeTx?.txHash, oat?.data?.relayTxs, onSuccess]);
317
- // Reset flag when orderId changes
318
- (0, react_5.useEffect)(() => {
319
- onSuccessCalled.current = false;
320
- }, [orderId]);
273
+ // Call onSuccess when order is executed
274
+ (0, useOnOrderSuccess_1.useOnOrderSuccess)({ orderData: oat, orderId, onSuccess });
321
275
  const { createOrder: createRegularOrder, isCreatingOrder: isCreatingRegularOrder } = (0, react_1.useAnyspendCreateOrder)({
322
276
  onSuccess: data => {
323
277
  setOrderId(data.data.id);
@@ -11,6 +11,7 @@ const qrcode_react_1 = require("qrcode.react");
11
11
  const react_2 = require("react");
12
12
  const useAnyspendOrderAndTransactions_1 = require("../hooks/useAnyspendOrderAndTransactions");
13
13
  const useCreateDepositFirstOrder_1 = require("../hooks/useCreateDepositFirstOrder");
14
+ const useOnOrderSuccess_1 = require("../hooks/useOnOrderSuccess");
14
15
  const useWatchTransfer_1 = require("../hooks/useWatchTransfer");
15
16
  const ChainTokenIcon_1 = require("./common/ChainTokenIcon");
16
17
  const OrderDetails_1 = require("./common/OrderDetails");
@@ -46,7 +47,6 @@ function QRDeposit({ mode = "modal", recipientAddress, sourceToken: sourceTokenP
46
47
  const [orderId, setOrderId] = (0, react_2.useState)();
47
48
  const [globalAddress, setGlobalAddress] = (0, react_2.useState)();
48
49
  const orderCreatedRef = (0, react_2.useRef)(false);
49
- const onSuccessCalled = (0, react_2.useRef)(false);
50
50
  const [transferResult, setTransferResult] = (0, react_2.useState)(null);
51
51
  // Source token/chain as state (can be changed by user)
52
52
  const [sourceChainId, setSourceChainId] = (0, react_2.useState)(sourceChainIdProp ?? 8453);
@@ -128,17 +128,7 @@ function QRDeposit({ mode = "modal", recipientAddress, sourceToken: sourceTokenP
128
128
  isPureTransfer,
129
129
  ]);
130
130
  // Call onSuccess when order is executed
131
- (0, react_2.useEffect)(() => {
132
- if (oat?.data?.order.status === "executed" && !onSuccessCalled.current) {
133
- const txHash = oat?.data?.executeTx?.txHash;
134
- onSuccess?.(txHash);
135
- onSuccessCalled.current = true;
136
- }
137
- }, [oat?.data?.order.status, oat?.data?.executeTx?.txHash, onSuccess]);
138
- // Reset onSuccess flag when orderId changes
139
- (0, react_2.useEffect)(() => {
140
- onSuccessCalled.current = false;
141
- }, [orderId]);
131
+ (0, useOnOrderSuccess_1.useOnOrderSuccess)({ orderData: oat, orderId, onSuccess });
142
132
  // For pure transfers, always use recipient address; for orders, use global address
143
133
  const displayAddress = isPureTransfer ? recipientAddress : globalAddress || recipientAddress;
144
134
  const handleCopyAddress = async () => {
@@ -12,6 +12,7 @@ export * from "./useGasPrice";
12
12
  export * from "./useGeoOnrampOptions";
13
13
  export * from "./useGetGeo";
14
14
  export * from "./useHyperliquidTransfer";
15
+ export * from "./useOnOrderSuccess";
15
16
  export * from "./useRecipientAddressState";
16
17
  export * from "./useSigMint";
17
18
  export * from "./useStripeClientSecret";
@@ -28,6 +28,7 @@ __exportStar(require("./useGasPrice"), exports);
28
28
  __exportStar(require("./useGeoOnrampOptions"), exports);
29
29
  __exportStar(require("./useGetGeo"), exports);
30
30
  __exportStar(require("./useHyperliquidTransfer"), exports);
31
+ __exportStar(require("./useOnOrderSuccess"), exports);
31
32
  __exportStar(require("./useRecipientAddressState"), exports);
32
33
  __exportStar(require("./useSigMint"), exports);
33
34
  __exportStar(require("./useStripeClientSecret"), exports);
@@ -0,0 +1,10 @@
1
+ import { GetOrderAndTxsResponse } from "../../types/api_req_res";
2
+ /**
3
+ * Hook to call onSuccess callback when an order is executed.
4
+ * Handles fallback to relayTxs when executeTx is null.
5
+ */
6
+ export declare function useOnOrderSuccess({ orderData, orderId, onSuccess, }: {
7
+ orderData: GetOrderAndTxsResponse | undefined;
8
+ orderId: string | undefined;
9
+ onSuccess?: (txHash?: string) => void;
10
+ }): void;
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useOnOrderSuccess = useOnOrderSuccess;
4
+ const react_1 = require("react");
5
+ /**
6
+ * Hook to call onSuccess callback when an order is executed.
7
+ * Handles fallback to relayTxs when executeTx is null.
8
+ */
9
+ function useOnOrderSuccess({ orderData, orderId, onSuccess, }) {
10
+ const onSuccessCalled = (0, react_1.useRef)(false);
11
+ const prevOrderId = (0, react_1.useRef)(orderId);
12
+ (0, react_1.useEffect)(() => {
13
+ // Reset flag when orderId changes
14
+ if (prevOrderId.current !== orderId) {
15
+ onSuccessCalled.current = false;
16
+ prevOrderId.current = orderId;
17
+ }
18
+ // Call onSuccess when order is executed
19
+ if (orderData?.data?.order.status === "executed" && !onSuccessCalled.current) {
20
+ const relayTxs = orderData?.data?.relayTxs;
21
+ const lastSuccessfulRelayTx = relayTxs?.filter(tx => tx.status === "success").pop();
22
+ const txHash = orderData?.data?.executeTx?.txHash || lastSuccessfulRelayTx?.txHash;
23
+ onSuccess?.(txHash);
24
+ onSuccessCalled.current = true;
25
+ }
26
+ }, [orderData, orderId, onSuccess]);
27
+ }
@@ -14,6 +14,16 @@ function useUserQuery() {
14
14
  const user = (0, userStore_1.useUserStore)(state => state.user);
15
15
  const setUserStore = (0, userStore_1.useUserStore)(state => state.setUser);
16
16
  const clearUserStore = (0, userStore_1.useUserStore)(state => state.clearUser);
17
+ // Manually rehydrate persisted store inside useLayoutEffect to avoid
18
+ // updating AuthenticationProvider state during Hydrate render.
19
+ // useLayoutEffect (not useEffect) ensures rehydration triggers a
20
+ // synchronous re-render before any useEffect callbacks fire, so
21
+ // downstream effects always see the persisted user value.
22
+ (0, react_1.useLayoutEffect)(() => {
23
+ if (!userStore_1.useUserStore.persist.hasHydrated()) {
24
+ userStore_1.useUserStore.persist.rehydrate();
25
+ }
26
+ }, []);
17
27
  // Listen for storage events from other tabs/windows
18
28
  (0, react_1.useEffect)(() => {
19
29
  const handleStorageChange = (e) => {
@@ -22,6 +22,7 @@ exports.useUserStore = (0, zustand_1.create)()((0, middleware_1.persist)(set =>
22
22
  },
23
23
  }), {
24
24
  name: "b3-user",
25
+ skipHydration: true,
25
26
  onRehydrateStorage: () => (_, error) => {
26
27
  if (error) {
27
28
  console.warn("Failed to rehydrate user store:", error);
@@ -18,6 +18,7 @@ import { useAutoSelectCryptoPaymentMethod } from "../hooks/useAutoSelectCryptoPa
18
18
  import { useConnectedWalletDisplay } from "../hooks/useConnectedWalletDisplay.js";
19
19
  import { useCryptoPaymentMethodState } from "../hooks/useCryptoPaymentMethodState.js";
20
20
  import { useDirectTransfer } from "../hooks/useDirectTransfer.js";
21
+ import { useOnOrderSuccess } from "../hooks/useOnOrderSuccess.js";
21
22
  import { useRecipientAddressState } from "../hooks/useRecipientAddressState.js";
22
23
  import { AnySpendFingerprintWrapper, getFingerprintConfig } from "./AnySpendFingerprintWrapper.js";
23
24
  import { CryptoPaymentMethod, CryptoPaymentMethodType } from "./common/CryptoPaymentMethod.js";
@@ -65,8 +66,6 @@ function AnySpendInner({ sourceChainId, destinationTokenAddress, destinationToke
65
66
  // Add refs to track URL state
66
67
  const initialUrlProcessed = useRef(false);
67
68
  const lastUrlUpdate = useRef(null);
68
- // Track if onSuccess has been called for the current order
69
- const onSuccessCalled = useRef(false);
70
69
  // Track animation direction for TransitionPanel
71
70
  const animationDirection = useRef(null);
72
71
  // Track previous panel for proper back navigation
@@ -486,18 +485,8 @@ function AnySpendInner({ sourceChainId, destinationTokenAddress, destinationToke
486
485
  }
487
486
  }
488
487
  }, [anyspendQuote, isSrcInputDirty, destinationTokenAmount]);
489
- useEffect(() => {
490
- if (oat?.data?.order.status === "executed" && !onSuccessCalled.current) {
491
- console.log("Calling onSuccess");
492
- const txHash = oat?.data?.executeTx?.txHash;
493
- onSuccess?.(txHash);
494
- onSuccessCalled.current = true;
495
- }
496
- }, [oat?.data?.order.status, oat?.data?.executeTx?.txHash, onSuccess]);
497
- // Reset flag when orderId changes
498
- useEffect(() => {
499
- onSuccessCalled.current = false;
500
- }, [orderId]);
488
+ // Call onSuccess when order is executed
489
+ useOnOrderSuccess({ orderData: oat, orderId, onSuccess });
501
490
  const { createOrder, isCreatingOrder } = useAnyspendCreateOrder({
502
491
  onSuccess: data => {
503
492
  const orderId = data.data.id;
@@ -11,9 +11,10 @@ import { simpleHashChainToChainName } from "../../../shared/utils/simplehash.js"
11
11
  import invariant from "invariant";
12
12
  import { ChevronRight, ChevronRightCircle, Info, Loader2 } from "lucide-react";
13
13
  import { motion } from "motion/react";
14
- import React, { useCallback, useEffect, useMemo, useState } from "react";
14
+ import { useCallback, useEffect, useMemo, useState } from "react";
15
15
  import { base } from "viem/chains";
16
16
  import { useCryptoPaymentMethodState } from "../hooks/useCryptoPaymentMethodState.js";
17
+ import { useOnOrderSuccess } from "../hooks/useOnOrderSuccess.js";
17
18
  import { useRecipientAddressState } from "../hooks/useRecipientAddressState.js";
18
19
  import { AnySpendFingerprintWrapper, getFingerprintConfig } from "./AnySpendFingerprintWrapper.js";
19
20
  import { CryptoPaymentMethod, CryptoPaymentMethodType } from "./common/CryptoPaymentMethod.js";
@@ -126,8 +127,6 @@ function AnySpendCustomInner({ loadOrder, mode = "modal", activeTab: activeTabPr
126
127
  globalAddress: currentWallet.address,
127
128
  });
128
129
  const [orderId, setOrderId] = useState(loadOrder);
129
- // Track if onSuccess has been called for the current order
130
- const onSuccessCalled = React.useRef(false);
131
130
  const [srcChainId, setSrcChainId] = useState(base.id);
132
131
  // Get token list for token balance check
133
132
  const chainName = useMemo(() => simpleHashChainToChainName(srcChainId), [srcChainId]);
@@ -265,20 +264,8 @@ function AnySpendCustomInner({ loadOrder, mode = "modal", activeTab: activeTabPr
265
264
  }, [activeTab, srcFiatAmountForGeoCheck]);
266
265
  // Get geo data and onramp options (use srcFiatAmountForGeoCheck to check availability regardless of activeTab)
267
266
  const { geoData, isOnrampSupported, coinbaseAvailablePaymentMethods, stripeOnrampSupport, stripeWeb2Support } = useGeoOnrampOptions(srcFiatAmountForGeoCheck);
268
- useEffect(() => {
269
- if (oat?.data?.order.status === "executed" && !onSuccessCalled.current) {
270
- console.log("Calling onSuccess");
271
- const relayTxs = oat?.data?.relayTxs;
272
- const lastRelayTxHash = relayTxs?.[relayTxs.length - 1]?.txHash;
273
- const txHash = oat?.data?.executeTx?.txHash || lastRelayTxHash;
274
- onSuccess?.(txHash);
275
- onSuccessCalled.current = true;
276
- }
277
- }, [oat?.data?.order.status, oat?.data?.executeTx?.txHash, oat?.data?.relayTxs, onSuccess]);
278
- // Reset flag when orderId changes
279
- useEffect(() => {
280
- onSuccessCalled.current = false;
281
- }, [orderId]);
267
+ // Call onSuccess when order is executed
268
+ useOnOrderSuccess({ orderData: oat, orderId, onSuccess });
282
269
  const { createOrder: createRegularOrder, isCreatingOrder: isCreatingRegularOrder } = useAnyspendCreateOrder({
283
270
  onSuccess: data => {
284
271
  setOrderId(data.data.id);
@@ -8,6 +8,7 @@ import { QRCodeSVG } from "qrcode.react";
8
8
  import { useEffect, useRef, useState } from "react";
9
9
  import { useAnyspendOrderAndTransactions } from "../hooks/useAnyspendOrderAndTransactions.js";
10
10
  import { useCreateDepositFirstOrder } from "../hooks/useCreateDepositFirstOrder.js";
11
+ import { useOnOrderSuccess } from "../hooks/useOnOrderSuccess.js";
11
12
  import { useWatchTransfer } from "../hooks/useWatchTransfer.js";
12
13
  import { ChainTokenIcon } from "./common/ChainTokenIcon.js";
13
14
  import { OrderDetails } from "./common/OrderDetails.js";
@@ -43,7 +44,6 @@ export function QRDeposit({ mode = "modal", recipientAddress, sourceToken: sourc
43
44
  const [orderId, setOrderId] = useState();
44
45
  const [globalAddress, setGlobalAddress] = useState();
45
46
  const orderCreatedRef = useRef(false);
46
- const onSuccessCalled = useRef(false);
47
47
  const [transferResult, setTransferResult] = useState(null);
48
48
  // Source token/chain as state (can be changed by user)
49
49
  const [sourceChainId, setSourceChainId] = useState(sourceChainIdProp ?? 8453);
@@ -125,17 +125,7 @@ export function QRDeposit({ mode = "modal", recipientAddress, sourceToken: sourc
125
125
  isPureTransfer,
126
126
  ]);
127
127
  // Call onSuccess when order is executed
128
- useEffect(() => {
129
- if (oat?.data?.order.status === "executed" && !onSuccessCalled.current) {
130
- const txHash = oat?.data?.executeTx?.txHash;
131
- onSuccess?.(txHash);
132
- onSuccessCalled.current = true;
133
- }
134
- }, [oat?.data?.order.status, oat?.data?.executeTx?.txHash, onSuccess]);
135
- // Reset onSuccess flag when orderId changes
136
- useEffect(() => {
137
- onSuccessCalled.current = false;
138
- }, [orderId]);
128
+ useOnOrderSuccess({ orderData: oat, orderId, onSuccess });
139
129
  // For pure transfers, always use recipient address; for orders, use global address
140
130
  const displayAddress = isPureTransfer ? recipientAddress : globalAddress || recipientAddress;
141
131
  const handleCopyAddress = async () => {
@@ -12,6 +12,7 @@ export * from "./useGasPrice";
12
12
  export * from "./useGeoOnrampOptions";
13
13
  export * from "./useGetGeo";
14
14
  export * from "./useHyperliquidTransfer";
15
+ export * from "./useOnOrderSuccess";
15
16
  export * from "./useRecipientAddressState";
16
17
  export * from "./useSigMint";
17
18
  export * from "./useStripeClientSecret";
@@ -12,6 +12,7 @@ export * from "./useGasPrice.js";
12
12
  export * from "./useGeoOnrampOptions.js";
13
13
  export * from "./useGetGeo.js";
14
14
  export * from "./useHyperliquidTransfer.js";
15
+ export * from "./useOnOrderSuccess.js";
15
16
  export * from "./useRecipientAddressState.js";
16
17
  export * from "./useSigMint.js";
17
18
  export * from "./useStripeClientSecret.js";
@@ -0,0 +1,10 @@
1
+ import { GetOrderAndTxsResponse } from "../../types/api_req_res";
2
+ /**
3
+ * Hook to call onSuccess callback when an order is executed.
4
+ * Handles fallback to relayTxs when executeTx is null.
5
+ */
6
+ export declare function useOnOrderSuccess({ orderData, orderId, onSuccess, }: {
7
+ orderData: GetOrderAndTxsResponse | undefined;
8
+ orderId: string | undefined;
9
+ onSuccess?: (txHash?: string) => void;
10
+ }): void;
@@ -0,0 +1,24 @@
1
+ import { useEffect, useRef } from "react";
2
+ /**
3
+ * Hook to call onSuccess callback when an order is executed.
4
+ * Handles fallback to relayTxs when executeTx is null.
5
+ */
6
+ export function useOnOrderSuccess({ orderData, orderId, onSuccess, }) {
7
+ const onSuccessCalled = useRef(false);
8
+ const prevOrderId = useRef(orderId);
9
+ useEffect(() => {
10
+ // Reset flag when orderId changes
11
+ if (prevOrderId.current !== orderId) {
12
+ onSuccessCalled.current = false;
13
+ prevOrderId.current = orderId;
14
+ }
15
+ // Call onSuccess when order is executed
16
+ if (orderData?.data?.order.status === "executed" && !onSuccessCalled.current) {
17
+ const relayTxs = orderData?.data?.relayTxs;
18
+ const lastSuccessfulRelayTx = relayTxs?.filter(tx => tx.status === "success").pop();
19
+ const txHash = orderData?.data?.executeTx?.txHash || lastSuccessfulRelayTx?.txHash;
20
+ onSuccess?.(txHash);
21
+ onSuccessCalled.current = true;
22
+ }
23
+ }, [orderData, orderId, onSuccess]);
24
+ }
@@ -1,4 +1,4 @@
1
- import { useEffect } from "react";
1
+ import { useEffect, useLayoutEffect } from "react";
2
2
  import { useUserStore } from "../stores/userStore.js";
3
3
  const USER_QUERY_KEY = ["b3-user"];
4
4
  /**
@@ -11,6 +11,16 @@ export function useUserQuery() {
11
11
  const user = useUserStore(state => state.user);
12
12
  const setUserStore = useUserStore(state => state.setUser);
13
13
  const clearUserStore = useUserStore(state => state.clearUser);
14
+ // Manually rehydrate persisted store inside useLayoutEffect to avoid
15
+ // updating AuthenticationProvider state during Hydrate render.
16
+ // useLayoutEffect (not useEffect) ensures rehydration triggers a
17
+ // synchronous re-render before any useEffect callbacks fire, so
18
+ // downstream effects always see the persisted user value.
19
+ useLayoutEffect(() => {
20
+ if (!useUserStore.persist.hasHydrated()) {
21
+ useUserStore.persist.rehydrate();
22
+ }
23
+ }, []);
14
24
  // Listen for storage events from other tabs/windows
15
25
  useEffect(() => {
16
26
  const handleStorageChange = (e) => {
@@ -19,6 +19,7 @@ export const useUserStore = create()(persist(set => ({
19
19
  },
20
20
  }), {
21
21
  name: "b3-user",
22
+ skipHydration: true,
22
23
  onRehydrateStorage: () => (_, error) => {
23
24
  if (error) {
24
25
  console.warn("Failed to rehydrate user store:", error);
@@ -12,6 +12,7 @@ export * from "./useGasPrice";
12
12
  export * from "./useGeoOnrampOptions";
13
13
  export * from "./useGetGeo";
14
14
  export * from "./useHyperliquidTransfer";
15
+ export * from "./useOnOrderSuccess";
15
16
  export * from "./useRecipientAddressState";
16
17
  export * from "./useSigMint";
17
18
  export * from "./useStripeClientSecret";
@@ -0,0 +1,10 @@
1
+ import { GetOrderAndTxsResponse } from "../../types/api_req_res";
2
+ /**
3
+ * Hook to call onSuccess callback when an order is executed.
4
+ * Handles fallback to relayTxs when executeTx is null.
5
+ */
6
+ export declare function useOnOrderSuccess({ orderData, orderId, onSuccess, }: {
7
+ orderData: GetOrderAndTxsResponse | undefined;
8
+ orderId: string | undefined;
9
+ onSuccess?: (txHash?: string) => void;
10
+ }): void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@b3dotfun/sdk",
3
- "version": "0.1.65-alpha.0",
3
+ "version": "0.1.65-alpha.2",
4
4
  "source": "src/index.ts",
5
5
  "main": "./dist/cjs/index.js",
6
6
  "react-native": "./dist/cjs/index.native.js",
@@ -52,6 +52,7 @@ import { useAutoSelectCryptoPaymentMethod } from "../hooks/useAutoSelectCryptoPa
52
52
  import { useConnectedWalletDisplay } from "../hooks/useConnectedWalletDisplay";
53
53
  import { useCryptoPaymentMethodState } from "../hooks/useCryptoPaymentMethodState";
54
54
  import { useDirectTransfer } from "../hooks/useDirectTransfer";
55
+ import { useOnOrderSuccess } from "../hooks/useOnOrderSuccess";
55
56
  import { useRecipientAddressState } from "../hooks/useRecipientAddressState";
56
57
  import { AnySpendFingerprintWrapper, getFingerprintConfig } from "./AnySpendFingerprintWrapper";
57
58
  import { CryptoPaymentMethod, CryptoPaymentMethodType } from "./common/CryptoPaymentMethod";
@@ -208,9 +209,6 @@ function AnySpendInner({
208
209
  toAmount?: string;
209
210
  } | null>(null);
210
211
 
211
- // Track if onSuccess has been called for the current order
212
- const onSuccessCalled = useRef(false);
213
-
214
212
  // Track animation direction for TransitionPanel
215
213
  const animationDirection = useRef<"forward" | "back" | null>(null);
216
214
  // Track previous panel for proper back navigation
@@ -704,19 +702,8 @@ function AnySpendInner({
704
702
  }
705
703
  }, [anyspendQuote, isSrcInputDirty, destinationTokenAmount]);
706
704
 
707
- useEffect(() => {
708
- if (oat?.data?.order.status === "executed" && !onSuccessCalled.current) {
709
- console.log("Calling onSuccess");
710
- const txHash = oat?.data?.executeTx?.txHash;
711
- onSuccess?.(txHash);
712
- onSuccessCalled.current = true;
713
- }
714
- }, [oat?.data?.order.status, oat?.data?.executeTx?.txHash, onSuccess]);
715
-
716
- // Reset flag when orderId changes
717
- useEffect(() => {
718
- onSuccessCalled.current = false;
719
- }, [orderId]);
705
+ // Call onSuccess when order is executed
706
+ useOnOrderSuccess({ orderData: oat, orderId, onSuccess });
720
707
 
721
708
  const { createOrder, isCreatingOrder } = useAnyspendCreateOrder({
722
709
  onSuccess: data => {
@@ -45,6 +45,7 @@ import React, { useCallback, useEffect, useMemo, useState } from "react";
45
45
 
46
46
  import { base } from "viem/chains";
47
47
  import { useCryptoPaymentMethodState } from "../hooks/useCryptoPaymentMethodState";
48
+ import { useOnOrderSuccess } from "../hooks/useOnOrderSuccess";
48
49
  import { useRecipientAddressState } from "../hooks/useRecipientAddressState";
49
50
  import { AnySpendFingerprintWrapper, getFingerprintConfig } from "./AnySpendFingerprintWrapper";
50
51
  import { CryptoPaymentMethod, CryptoPaymentMethodType } from "./common/CryptoPaymentMethod";
@@ -274,9 +275,6 @@ function AnySpendCustomInner({
274
275
 
275
276
  const [orderId, setOrderId] = useState<string | undefined>(loadOrder);
276
277
 
277
- // Track if onSuccess has been called for the current order
278
- const onSuccessCalled = React.useRef(false);
279
-
280
278
  const [srcChainId, setSrcChainId] = useState<number>(base.id);
281
279
 
282
280
  // Get token list for token balance check
@@ -433,21 +431,8 @@ function AnySpendCustomInner({
433
431
  const { geoData, isOnrampSupported, coinbaseAvailablePaymentMethods, stripeOnrampSupport, stripeWeb2Support } =
434
432
  useGeoOnrampOptions(srcFiatAmountForGeoCheck);
435
433
 
436
- useEffect(() => {
437
- if (oat?.data?.order.status === "executed" && !onSuccessCalled.current) {
438
- console.log("Calling onSuccess");
439
- const relayTxs = oat?.data?.relayTxs;
440
- const lastRelayTxHash = relayTxs?.[relayTxs.length - 1]?.txHash;
441
- const txHash = oat?.data?.executeTx?.txHash || lastRelayTxHash;
442
- onSuccess?.(txHash);
443
- onSuccessCalled.current = true;
444
- }
445
- }, [oat?.data?.order.status, oat?.data?.executeTx?.txHash, oat?.data?.relayTxs, onSuccess]);
446
-
447
- // Reset flag when orderId changes
448
- useEffect(() => {
449
- onSuccessCalled.current = false;
450
- }, [orderId]);
434
+ // Call onSuccess when order is executed
435
+ useOnOrderSuccess({ orderData: oat, orderId, onSuccess });
451
436
 
452
437
  const { createOrder: createRegularOrder, isCreatingOrder: isCreatingRegularOrder } = useAnyspendCreateOrder({
453
438
  onSuccess: data => {
@@ -8,6 +8,7 @@ import { QRCodeSVG } from "qrcode.react";
8
8
  import { useEffect, useRef, useState } from "react";
9
9
  import { useAnyspendOrderAndTransactions } from "../hooks/useAnyspendOrderAndTransactions";
10
10
  import { useCreateDepositFirstOrder } from "../hooks/useCreateDepositFirstOrder";
11
+ import { useOnOrderSuccess } from "../hooks/useOnOrderSuccess";
11
12
  import { TransferResult, useWatchTransfer } from "../hooks/useWatchTransfer";
12
13
  import { DepositContractConfig } from "./AnySpendDeposit";
13
14
  import { ChainTokenIcon } from "./common/ChainTokenIcon";
@@ -90,7 +91,6 @@ export function QRDeposit({
90
91
  const [orderId, setOrderId] = useState<string | undefined>();
91
92
  const [globalAddress, setGlobalAddress] = useState<string | undefined>();
92
93
  const orderCreatedRef = useRef(false);
93
- const onSuccessCalled = useRef(false);
94
94
  const [transferResult, setTransferResult] = useState<TransferResult | null>(null);
95
95
 
96
96
  // Source token/chain as state (can be changed by user)
@@ -189,18 +189,7 @@ export function QRDeposit({
189
189
  ]);
190
190
 
191
191
  // Call onSuccess when order is executed
192
- useEffect(() => {
193
- if (oat?.data?.order.status === "executed" && !onSuccessCalled.current) {
194
- const txHash = oat?.data?.executeTx?.txHash;
195
- onSuccess?.(txHash);
196
- onSuccessCalled.current = true;
197
- }
198
- }, [oat?.data?.order.status, oat?.data?.executeTx?.txHash, onSuccess]);
199
-
200
- // Reset onSuccess flag when orderId changes
201
- useEffect(() => {
202
- onSuccessCalled.current = false;
203
- }, [orderId]);
192
+ useOnOrderSuccess({ orderData: oat, orderId, onSuccess });
204
193
 
205
194
  // For pure transfers, always use recipient address; for orders, use global address
206
195
  const displayAddress = isPureTransfer ? recipientAddress : globalAddress || recipientAddress;
@@ -12,6 +12,7 @@ export * from "./useGasPrice";
12
12
  export * from "./useGeoOnrampOptions";
13
13
  export * from "./useGetGeo";
14
14
  export * from "./useHyperliquidTransfer";
15
+ export * from "./useOnOrderSuccess";
15
16
  export * from "./useRecipientAddressState";
16
17
  export * from "./useSigMint";
17
18
  export * from "./useStripeClientSecret";
@@ -0,0 +1,36 @@
1
+ import { useEffect, useRef } from "react";
2
+ import { GetOrderAndTxsResponse } from "../../types/api_req_res";
3
+
4
+ /**
5
+ * Hook to call onSuccess callback when an order is executed.
6
+ * Handles fallback to relayTxs when executeTx is null.
7
+ */
8
+ export function useOnOrderSuccess({
9
+ orderData,
10
+ orderId,
11
+ onSuccess,
12
+ }: {
13
+ orderData: GetOrderAndTxsResponse | undefined;
14
+ orderId: string | undefined;
15
+ onSuccess?: (txHash?: string) => void;
16
+ }) {
17
+ const onSuccessCalled = useRef(false);
18
+ const prevOrderId = useRef(orderId);
19
+
20
+ useEffect(() => {
21
+ // Reset flag when orderId changes
22
+ if (prevOrderId.current !== orderId) {
23
+ onSuccessCalled.current = false;
24
+ prevOrderId.current = orderId;
25
+ }
26
+
27
+ // Call onSuccess when order is executed
28
+ if (orderData?.data?.order.status === "executed" && !onSuccessCalled.current) {
29
+ const relayTxs = orderData?.data?.relayTxs;
30
+ const lastSuccessfulRelayTx = relayTxs?.filter(tx => tx.status === "success").pop();
31
+ const txHash = orderData?.data?.executeTx?.txHash || lastSuccessfulRelayTx?.txHash;
32
+ onSuccess?.(txHash);
33
+ onSuccessCalled.current = true;
34
+ }
35
+ }, [orderData, orderId, onSuccess]);
36
+ }
@@ -1,5 +1,5 @@
1
1
  import { Users } from "@b3dotfun/b3-api";
2
- import { useEffect } from "react";
2
+ import { useEffect, useLayoutEffect } from "react";
3
3
  import { useUserStore } from "../stores/userStore";
4
4
 
5
5
  const USER_QUERY_KEY = ["b3-user"];
@@ -15,6 +15,17 @@ export function useUserQuery() {
15
15
  const setUserStore = useUserStore(state => state.setUser);
16
16
  const clearUserStore = useUserStore(state => state.clearUser);
17
17
 
18
+ // Manually rehydrate persisted store inside useLayoutEffect to avoid
19
+ // updating AuthenticationProvider state during Hydrate render.
20
+ // useLayoutEffect (not useEffect) ensures rehydration triggers a
21
+ // synchronous re-render before any useEffect callbacks fire, so
22
+ // downstream effects always see the persisted user value.
23
+ useLayoutEffect(() => {
24
+ if (!useUserStore.persist.hasHydrated()) {
25
+ useUserStore.persist.rehydrate();
26
+ }
27
+ }, []);
28
+
18
29
  // Listen for storage events from other tabs/windows
19
30
  useEffect(() => {
20
31
  const handleStorageChange = (e: StorageEvent) => {
@@ -31,6 +31,7 @@ export const useUserStore = create<UserStore>()(
31
31
  }),
32
32
  {
33
33
  name: "b3-user",
34
+ skipHydration: true,
34
35
  onRehydrateStorage: () => (_, error) => {
35
36
  if (error) {
36
37
  console.warn("Failed to rehydrate user store:", error);