@b3dotfun/sdk 0.1.0 → 0.1.1-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (171) hide show
  1. package/dist/cjs/anyspend/react/components/AnySpend.js +1 -1
  2. package/dist/cjs/anyspend/react/components/AnySpendDeposit.d.ts +15 -10
  3. package/dist/cjs/anyspend/react/components/AnySpendDeposit.js +22 -14
  4. package/dist/cjs/anyspend/react/components/QRDeposit.js +31 -5
  5. package/dist/cjs/anyspend/react/components/common/CryptoPaymentMethod.js +9 -2
  6. package/dist/cjs/anyspend/react/components/common/GasIndicator.d.ts +1 -1
  7. package/dist/cjs/anyspend/react/components/common/GasIndicator.js +6 -16
  8. package/dist/cjs/anyspend/react/components/common/TokenBalance.js +15 -4
  9. package/dist/cjs/anyspend/react/components/common/TransferResultScreen.d.ts +22 -0
  10. package/dist/cjs/anyspend/react/components/common/TransferResultScreen.js +25 -0
  11. package/dist/cjs/anyspend/react/hooks/index.d.ts +1 -0
  12. package/dist/cjs/anyspend/react/hooks/index.js +1 -0
  13. package/dist/cjs/anyspend/react/hooks/useWatchTransfer.d.ts +41 -0
  14. package/dist/cjs/anyspend/react/hooks/useWatchTransfer.js +75 -0
  15. package/dist/cjs/anyspend/react/providers/AnyspendProvider.js +1 -2
  16. package/dist/cjs/anyspend/utils/address.d.ts +5 -0
  17. package/dist/cjs/anyspend/utils/address.js +8 -0
  18. package/dist/cjs/global-account/react/components/AccountAssets/AccountAssets.js +7 -3
  19. package/dist/cjs/global-account/react/components/B3DynamicModal.js +4 -14
  20. package/dist/cjs/global-account/react/components/B3Provider/B3ConfigProvider.d.ts +31 -0
  21. package/dist/cjs/global-account/react/components/B3Provider/B3ConfigProvider.js +37 -0
  22. package/dist/cjs/global-account/react/components/B3Provider/B3Provider.d.ts +2 -1
  23. package/dist/cjs/global-account/react/components/B3Provider/B3Provider.js +3 -31
  24. package/dist/cjs/global-account/react/components/B3Provider/B3Provider.native.js +4 -31
  25. package/dist/cjs/global-account/react/components/B3Provider/LocalSDKProvider.d.ts +3 -1
  26. package/dist/cjs/global-account/react/components/B3Provider/LocalSDKProvider.js +3 -1
  27. package/dist/cjs/global-account/react/components/B3Provider/useB3.d.ts +1 -12
  28. package/dist/cjs/global-account/react/components/B3Provider/useB3Config.d.ts +1 -17
  29. package/dist/cjs/global-account/react/components/B3Provider/useB3Config.js +2 -21
  30. package/dist/cjs/global-account/react/components/ManageAccount/NFTContent.js +2 -2
  31. package/dist/cjs/global-account/react/components/SignInWithB3/SignInWithB3Flow.js +12 -1
  32. package/dist/cjs/global-account/react/components/SingleUserSearchSelector/SingleUserSearchSelector.d.ts +64 -0
  33. package/dist/cjs/global-account/react/components/SingleUserSearchSelector/SingleUserSearchSelector.js +163 -0
  34. package/dist/cjs/global-account/react/components/SingleUserSearchSelector/index.d.ts +2 -0
  35. package/dist/cjs/global-account/react/components/SingleUserSearchSelector/index.js +5 -0
  36. package/dist/cjs/global-account/react/components/WalletImage/WalletImage.d.ts +1 -1
  37. package/dist/cjs/global-account/react/components/index.d.ts +2 -0
  38. package/dist/cjs/global-account/react/components/index.js +6 -3
  39. package/dist/cjs/global-account/react/hooks/index.d.ts +2 -1
  40. package/dist/cjs/global-account/react/hooks/index.js +5 -1
  41. package/dist/cjs/global-account/react/hooks/useAuthentication.js +5 -2
  42. package/dist/cjs/global-account/react/hooks/useProfile.js +4 -23
  43. package/dist/cjs/global-account/react/hooks/useSimBalance.d.ts +7 -0
  44. package/dist/cjs/global-account/react/hooks/useSimBalance.js +43 -11
  45. package/dist/cjs/global-account/react/hooks/useSimCollectibles.d.ts +45 -0
  46. package/dist/cjs/global-account/react/hooks/useSimCollectibles.js +190 -0
  47. package/dist/cjs/global-account/react/stores/index.d.ts +0 -1
  48. package/dist/cjs/global-account/react/stores/index.js +1 -3
  49. package/dist/cjs/global-account/react/stores/useModalStore.d.ts +63 -1
  50. package/dist/cjs/global-account/react/stores/useModalStore.js +3 -0
  51. package/dist/cjs/global-account/react/utils/profileApi.d.ts +13 -0
  52. package/dist/cjs/global-account/react/utils/profileApi.js +29 -0
  53. package/dist/cjs/global-account/react/utils/simdune.d.ts +7 -0
  54. package/dist/cjs/global-account/react/utils/simdune.js +21 -0
  55. package/dist/esm/anyspend/react/components/AnySpend.js +1 -1
  56. package/dist/esm/anyspend/react/components/AnySpendDeposit.d.ts +15 -10
  57. package/dist/esm/anyspend/react/components/AnySpendDeposit.js +23 -15
  58. package/dist/esm/anyspend/react/components/QRDeposit.js +32 -6
  59. package/dist/esm/anyspend/react/components/common/CryptoPaymentMethod.js +10 -3
  60. package/dist/esm/anyspend/react/components/common/GasIndicator.d.ts +1 -1
  61. package/dist/esm/anyspend/react/components/common/GasIndicator.js +7 -17
  62. package/dist/esm/anyspend/react/components/common/TokenBalance.js +16 -5
  63. package/dist/esm/anyspend/react/components/common/TransferResultScreen.d.ts +22 -0
  64. package/dist/esm/anyspend/react/components/common/TransferResultScreen.js +22 -0
  65. package/dist/esm/anyspend/react/hooks/index.d.ts +1 -0
  66. package/dist/esm/anyspend/react/hooks/index.js +1 -0
  67. package/dist/esm/anyspend/react/hooks/useWatchTransfer.d.ts +41 -0
  68. package/dist/esm/anyspend/react/hooks/useWatchTransfer.js +72 -0
  69. package/dist/esm/anyspend/react/providers/AnyspendProvider.js +1 -2
  70. package/dist/esm/anyspend/utils/address.d.ts +5 -0
  71. package/dist/esm/anyspend/utils/address.js +7 -0
  72. package/dist/esm/global-account/react/components/AccountAssets/AccountAssets.js +7 -3
  73. package/dist/esm/global-account/react/components/B3DynamicModal.js +5 -15
  74. package/dist/esm/global-account/react/components/B3Provider/B3ConfigProvider.d.ts +31 -0
  75. package/dist/esm/global-account/react/components/B3Provider/B3ConfigProvider.js +33 -0
  76. package/dist/esm/global-account/react/components/B3Provider/B3Provider.d.ts +2 -1
  77. package/dist/esm/global-account/react/components/B3Provider/B3Provider.js +3 -31
  78. package/dist/esm/global-account/react/components/B3Provider/B3Provider.native.js +3 -30
  79. package/dist/esm/global-account/react/components/B3Provider/LocalSDKProvider.d.ts +3 -1
  80. package/dist/esm/global-account/react/components/B3Provider/LocalSDKProvider.js +3 -1
  81. package/dist/esm/global-account/react/components/B3Provider/useB3.d.ts +1 -12
  82. package/dist/esm/global-account/react/components/B3Provider/useB3Config.d.ts +1 -17
  83. package/dist/esm/global-account/react/components/B3Provider/useB3Config.js +1 -20
  84. package/dist/esm/global-account/react/components/ManageAccount/NFTContent.js +3 -3
  85. package/dist/esm/global-account/react/components/SignInWithB3/SignInWithB3Flow.js +13 -2
  86. package/dist/esm/global-account/react/components/SingleUserSearchSelector/SingleUserSearchSelector.d.ts +64 -0
  87. package/dist/esm/global-account/react/components/SingleUserSearchSelector/SingleUserSearchSelector.js +160 -0
  88. package/dist/esm/global-account/react/components/SingleUserSearchSelector/index.d.ts +2 -0
  89. package/dist/esm/global-account/react/components/SingleUserSearchSelector/index.js +1 -0
  90. package/dist/esm/global-account/react/components/WalletImage/WalletImage.d.ts +1 -1
  91. package/dist/esm/global-account/react/components/index.d.ts +2 -0
  92. package/dist/esm/global-account/react/components/index.js +2 -0
  93. package/dist/esm/global-account/react/hooks/index.d.ts +2 -1
  94. package/dist/esm/global-account/react/hooks/index.js +2 -1
  95. package/dist/esm/global-account/react/hooks/useAuthentication.js +5 -2
  96. package/dist/esm/global-account/react/hooks/useProfile.js +1 -20
  97. package/dist/esm/global-account/react/hooks/useSimBalance.d.ts +7 -0
  98. package/dist/esm/global-account/react/hooks/useSimBalance.js +42 -11
  99. package/dist/esm/global-account/react/hooks/useSimCollectibles.d.ts +45 -0
  100. package/dist/esm/global-account/react/hooks/useSimCollectibles.js +187 -0
  101. package/dist/esm/global-account/react/stores/index.d.ts +0 -1
  102. package/dist/esm/global-account/react/stores/index.js +0 -1
  103. package/dist/esm/global-account/react/stores/useModalStore.d.ts +63 -1
  104. package/dist/esm/global-account/react/stores/useModalStore.js +3 -0
  105. package/dist/esm/global-account/react/utils/profileApi.d.ts +13 -0
  106. package/dist/esm/global-account/react/utils/profileApi.js +25 -0
  107. package/dist/esm/global-account/react/utils/simdune.d.ts +7 -0
  108. package/dist/esm/global-account/react/utils/simdune.js +17 -0
  109. package/dist/styles/index.css +1 -1
  110. package/dist/types/anyspend/react/components/AnySpendDeposit.d.ts +15 -10
  111. package/dist/types/anyspend/react/components/common/GasIndicator.d.ts +1 -1
  112. package/dist/types/anyspend/react/components/common/TransferResultScreen.d.ts +22 -0
  113. package/dist/types/anyspend/react/hooks/index.d.ts +1 -0
  114. package/dist/types/anyspend/react/hooks/useWatchTransfer.d.ts +41 -0
  115. package/dist/types/anyspend/utils/address.d.ts +5 -0
  116. package/dist/types/global-account/react/components/B3Provider/B3ConfigProvider.d.ts +31 -0
  117. package/dist/types/global-account/react/components/B3Provider/B3Provider.d.ts +2 -1
  118. package/dist/types/global-account/react/components/B3Provider/LocalSDKProvider.d.ts +3 -1
  119. package/dist/types/global-account/react/components/B3Provider/useB3.d.ts +1 -12
  120. package/dist/types/global-account/react/components/B3Provider/useB3Config.d.ts +1 -17
  121. package/dist/types/global-account/react/components/SingleUserSearchSelector/SingleUserSearchSelector.d.ts +64 -0
  122. package/dist/types/global-account/react/components/SingleUserSearchSelector/index.d.ts +2 -0
  123. package/dist/types/global-account/react/components/WalletImage/WalletImage.d.ts +1 -1
  124. package/dist/types/global-account/react/components/index.d.ts +2 -0
  125. package/dist/types/global-account/react/hooks/index.d.ts +2 -1
  126. package/dist/types/global-account/react/hooks/useSimBalance.d.ts +7 -0
  127. package/dist/types/global-account/react/hooks/useSimCollectibles.d.ts +45 -0
  128. package/dist/types/global-account/react/stores/index.d.ts +0 -1
  129. package/dist/types/global-account/react/stores/useModalStore.d.ts +63 -1
  130. package/dist/types/global-account/react/utils/profileApi.d.ts +13 -0
  131. package/dist/types/global-account/react/utils/simdune.d.ts +7 -0
  132. package/package.json +6 -1
  133. package/src/anyspend/react/components/AnySpend.tsx +1 -0
  134. package/src/anyspend/react/components/AnySpendDeposit.tsx +60 -42
  135. package/src/anyspend/react/components/QRDeposit.tsx +57 -5
  136. package/src/anyspend/react/components/common/CryptoPaymentMethod.tsx +13 -3
  137. package/src/anyspend/react/components/common/GasIndicator.tsx +11 -30
  138. package/src/anyspend/react/components/common/TokenBalance.tsx +17 -5
  139. package/src/anyspend/react/components/common/TransferResultScreen.tsx +107 -0
  140. package/src/anyspend/react/hooks/index.ts +1 -0
  141. package/src/anyspend/react/hooks/useWatchTransfer.ts +114 -0
  142. package/src/anyspend/react/providers/AnyspendProvider.tsx +2 -5
  143. package/src/anyspend/utils/address.ts +13 -0
  144. package/src/global-account/react/components/AccountAssets/AccountAssets.tsx +25 -13
  145. package/src/global-account/react/components/B3DynamicModal.tsx +5 -17
  146. package/src/global-account/react/components/B3Provider/B3ConfigProvider.tsx +84 -0
  147. package/src/global-account/react/components/B3Provider/B3Provider.native.tsx +28 -36
  148. package/src/global-account/react/components/B3Provider/B3Provider.tsx +24 -39
  149. package/src/global-account/react/components/B3Provider/LocalSDKProvider.tsx +5 -0
  150. package/src/global-account/react/components/B3Provider/useB3Config.ts +1 -21
  151. package/src/global-account/react/components/ManageAccount/NFTContent.tsx +4 -4
  152. package/src/global-account/react/components/SignInWithB3/SignInWithB3Flow.tsx +13 -2
  153. package/src/global-account/react/components/SingleUserSearchSelector/README.md +266 -0
  154. package/src/global-account/react/components/SingleUserSearchSelector/SingleUserSearchSelector.tsx +330 -0
  155. package/src/global-account/react/components/SingleUserSearchSelector/index.ts +2 -0
  156. package/src/global-account/react/components/index.ts +7 -0
  157. package/src/global-account/react/hooks/index.ts +2 -1
  158. package/src/global-account/react/hooks/useAuthentication.ts +6 -2
  159. package/src/global-account/react/hooks/useProfile.ts +1 -32
  160. package/src/global-account/react/hooks/useSimBalance.ts +49 -12
  161. package/src/global-account/react/hooks/useSimCollectibles.ts +238 -0
  162. package/src/global-account/react/stores/index.ts +0 -1
  163. package/src/global-account/react/stores/useModalStore.ts +67 -1
  164. package/src/global-account/react/utils/profileApi.ts +38 -0
  165. package/src/global-account/react/utils/simdune.ts +20 -0
  166. package/dist/cjs/global-account/react/stores/configStore.d.ts +0 -24
  167. package/dist/cjs/global-account/react/stores/configStore.js +0 -30
  168. package/dist/esm/global-account/react/stores/configStore.d.ts +0 -24
  169. package/dist/esm/global-account/react/stores/configStore.js +0 -27
  170. package/dist/types/global-account/react/stores/configStore.d.ts +0 -24
  171. package/src/global-account/react/stores/configStore.ts +0 -51
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { ALL_CHAINS, getAvailableChainIds } from "../../../anyspend/index.js";
2
+ import { ALL_CHAINS, getAvailableChainIds, isSameChainAndToken } from "../../../anyspend/index.js";
3
3
  import { Button, toast } from "../../../global-account/react/index.js";
4
4
  import { cn } from "../../../shared/utils/cn.js";
5
5
  import { TokenSelector } from "@relayprotocol/relay-kit-ui";
@@ -8,8 +8,10 @@ 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 { useWatchTransfer } from "../hooks/useWatchTransfer.js";
11
12
  import { ChainTokenIcon } from "./common/ChainTokenIcon.js";
12
13
  import { OrderDetails } from "./common/OrderDetails.js";
14
+ import { TransferResultScreen } from "./common/TransferResultScreen.js";
13
15
  import { ChainWarningText, WarningText } from "./common/WarningText.js";
14
16
  // Default source token: ETH on Base
15
17
  const DEFAULT_ETH_ON_BASE = {
@@ -42,9 +44,24 @@ export function QRDeposit({ mode = "modal", recipientAddress, sourceToken: sourc
42
44
  const [globalAddress, setGlobalAddress] = useState();
43
45
  const orderCreatedRef = useRef(false);
44
46
  const onSuccessCalled = useRef(false);
47
+ const [transferResult, setTransferResult] = useState(null);
45
48
  // Source token/chain as state (can be changed by user)
46
49
  const [sourceChainId, setSourceChainId] = useState(sourceChainIdProp ?? 8453);
47
50
  const [sourceToken, setSourceToken] = useState(sourceTokenProp ?? DEFAULT_ETH_ON_BASE);
51
+ // Check if this is a pure transfer (same chain and token)
52
+ const isPureTransfer = isSameChainAndToken(sourceChainId, sourceToken.address, destinationChainId, destinationToken.address);
53
+ // Watch for pure transfers (same chain and token)
54
+ const { isWatching: isWatchingTransfer } = useWatchTransfer({
55
+ address: recipientAddress,
56
+ chainId: sourceChainId,
57
+ tokenAddress: sourceToken.address,
58
+ tokenDecimals: sourceToken.decimals,
59
+ enabled: isPureTransfer && !transferResult,
60
+ onTransferDetected: result => {
61
+ setTransferResult(result);
62
+ onSuccess?.();
63
+ },
64
+ });
48
65
  // Handle token selection from TokenSelector
49
66
  const handleTokenSelect = (newToken) => {
50
67
  const token = {
@@ -59,6 +76,7 @@ export function QRDeposit({ mode = "modal", recipientAddress, sourceToken: sourc
59
76
  setOrderId(undefined);
60
77
  setGlobalAddress(undefined);
61
78
  orderCreatedRef.current = false;
79
+ setTransferResult(null);
62
80
  // Update token and chain
63
81
  setSourceChainId(newToken.chainId);
64
82
  setSourceToken(token);
@@ -79,10 +97,12 @@ export function QRDeposit({ mode = "modal", recipientAddress, sourceToken: sourc
79
97
  });
80
98
  // Fetch order status
81
99
  const { orderAndTransactions: oat } = useAnyspendOrderAndTransactions(orderId);
82
- // Create order on mount
100
+ // Create order on mount (skip for pure transfers)
83
101
  useEffect(() => {
84
102
  if (orderCreatedRef.current)
85
103
  return;
104
+ if (isPureTransfer)
105
+ return; // Skip order creation for pure transfers
86
106
  orderCreatedRef.current = true;
87
107
  createOrder({
88
108
  recipientAddress,
@@ -102,6 +122,7 @@ export function QRDeposit({ mode = "modal", recipientAddress, sourceToken: sourc
102
122
  creatorAddress,
103
123
  depositContractConfig,
104
124
  createOrder,
125
+ isPureTransfer,
105
126
  ]);
106
127
  // Call onSuccess when order is executed
107
128
  useEffect(() => {
@@ -115,7 +136,8 @@ export function QRDeposit({ mode = "modal", recipientAddress, sourceToken: sourc
115
136
  useEffect(() => {
116
137
  onSuccessCalled.current = false;
117
138
  }, [orderId]);
118
- const displayAddress = globalAddress || recipientAddress;
139
+ // For pure transfers, always use recipient address; for orders, use global address
140
+ const displayAddress = isPureTransfer ? recipientAddress : globalAddress || recipientAddress;
119
141
  const handleCopyAddress = async () => {
120
142
  if (displayAddress) {
121
143
  await navigator.clipboard.writeText(displayAddress);
@@ -131,13 +153,17 @@ export function QRDeposit({ mode = "modal", recipientAddress, sourceToken: sourc
131
153
  setCopied(false);
132
154
  onClose?.();
133
155
  };
156
+ // Show transfer result screen for completed pure transfers
157
+ if (isPureTransfer && transferResult) {
158
+ return (_jsx(TransferResultScreen, { mode: mode, transferResult: transferResult, token: sourceToken, chainId: sourceChainId, recipientAddress: recipientAddress, onBack: onBack, onClose: onClose }));
159
+ }
134
160
  // Show order details if order has deposits or is being processed
135
161
  if (oat?.data && oat.data.depositTxs && oat.data.depositTxs.length > 0) {
136
162
  return (_jsx("div", { className: cn("anyspend-container anyspend-qr-order-details font-inter bg-as-surface-primary mx-auto w-full max-w-[460px] p-6", mode === "page" && "border-as-border-secondary overflow-hidden rounded-2xl border shadow-xl"), children: _jsx("div", { className: "anyspend-qr-order-details-content relative flex flex-col gap-4", children: _jsx(OrderDetails, { mode: mode, order: oat.data.order, depositTxs: oat.data.depositTxs, relayTxs: oat.data.relayTxs, executeTx: oat.data.executeTx, refundTxs: oat.data.refundTxs, onBack: handleBack }) }) }));
137
163
  }
138
- // Show loading state while creating order (but not if we already have an orderId)
139
- if (isCreatingOrder && !orderId) {
164
+ // Show loading state while creating order (but not if we already have an orderId or for pure transfers)
165
+ if (isCreatingOrder && !orderId && !isPureTransfer) {
140
166
  return (_jsx("div", { className: cn("anyspend-container anyspend-qr-loading font-inter bg-as-surface-primary mx-auto w-full max-w-[460px] p-6", mode === "page" && "border-as-border-secondary overflow-hidden rounded-2xl border shadow-xl"), children: _jsxs("div", { className: "anyspend-qr-loading-content flex flex-col items-center justify-center gap-4 py-12", children: [_jsx(Loader2, { className: "anyspend-qr-loading-spinner text-as-brand h-8 w-8 animate-spin" }), _jsx("p", { className: "anyspend-qr-loading-text text-as-secondary text-sm", children: "Creating deposit order..." })] }) }));
141
167
  }
142
- return (_jsx("div", { className: cn("anyspend-container anyspend-qr-deposit font-inter bg-as-surface-primary mx-auto w-full max-w-[460px] p-6", mode === "page" && "border-as-border-secondary overflow-hidden rounded-2xl border shadow-xl"), children: _jsxs("div", { className: "anyspend-qr-deposit-content flex flex-col gap-4", children: [_jsxs("div", { className: "anyspend-qr-header flex items-center justify-between", children: [_jsx("button", { onClick: handleBack, className: "anyspend-qr-back-button text-as-secondary hover:text-as-primary", children: _jsx("svg", { className: "h-5 w-5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M15 19l-7-7 7-7" }) }) }), _jsx("h2", { className: "anyspend-qr-title text-as-primary text-base font-semibold", children: "Deposit" }), onClose ? (_jsx("button", { onClick: handleClose, className: "anyspend-qr-close-button text-as-secondary hover:text-as-primary", children: _jsx("svg", { className: "h-5 w-5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) }) })) : (_jsx("div", { className: "w-5" }))] }), _jsxs("div", { className: "anyspend-qr-token-selector flex flex-col gap-1.5", children: [_jsx("label", { className: "anyspend-qr-token-label text-as-secondary text-sm", children: "Send" }), _jsx(TokenSelector, { chainIdsFilter: getAvailableChainIds("from"), context: "from", fromChainWalletVMSupported: true, isValidAddress: true, lockedChainIds: getAvailableChainIds("from"), multiWalletSupportEnabled: true, onAnalyticEvent: undefined, setToken: handleTokenSelect, supportedWalletVMs: ["evm"], token: undefined, trigger: _jsxs(Button, { variant: "outline", role: "combobox", className: "anyspend-qr-token-trigger border-as-stroke bg-as-surface-secondary flex h-auto w-full items-center justify-between gap-2 rounded-xl border px-3 py-2.5", children: [_jsxs("div", { className: "flex items-center gap-2", children: [sourceToken.metadata?.logoURI ? (_jsx(ChainTokenIcon, { chainUrl: ALL_CHAINS[sourceChainId]?.logoUrl, tokenUrl: sourceToken.metadata.logoURI, className: "h-8 min-h-8 w-8 min-w-8" })) : (_jsx("div", { className: "h-8 w-8 rounded-full bg-gray-700" })), _jsxs("div", { className: "flex flex-col items-start gap-0", children: [_jsx("div", { className: "text-as-primary font-semibold", children: sourceToken.symbol }), _jsx("div", { className: "text-as-primary/70 text-xs", children: ALL_CHAINS[sourceChainId]?.name ?? "Unknown" })] })] }), _jsx(ChevronsUpDown, { className: "h-4 w-4 shrink-0 opacity-70" })] }) })] }), _jsxs("div", { className: "anyspend-qr-content border-as-stroke flex items-start gap-4 rounded-xl border p-4", children: [_jsxs("div", { className: "anyspend-qr-code-container flex flex-col items-center gap-2", children: [_jsx("div", { className: "anyspend-qr-code rounded-lg bg-white p-2", children: _jsx(QRCodeSVG, { value: displayAddress, size: 120, level: "M", marginSize: 0 }) }), _jsxs("span", { className: "anyspend-qr-scan-hint text-as-secondary text-xs", children: ["SCAN WITH ", _jsx("span", { className: "inline-block", children: "\uD83E\uDD8A" })] })] }), _jsxs("div", { className: "anyspend-qr-address-container flex flex-1 flex-col gap-1", children: [_jsx("span", { className: "anyspend-qr-address-label text-as-secondary text-sm", children: "Deposit address:" }), _jsxs("div", { className: "anyspend-qr-address-row flex items-start gap-1", children: [_jsx("span", { className: "anyspend-qr-address text-as-primary break-all font-mono text-sm leading-relaxed", children: displayAddress }), _jsx("button", { onClick: handleCopyAddress, className: "anyspend-qr-copy-icon text-as-secondary hover:text-as-primary mt-0.5 shrink-0", children: copied ? _jsx(Check, { className: "h-4 w-4" }) : _jsx(Copy, { className: "h-4 w-4" }) })] })] })] }), _jsx(ChainWarningText, { chainId: destinationChainId }), _jsxs(WarningText, { children: ["Only send ", sourceToken.symbol, " on ", ALL_CHAINS[sourceChainId]?.name ?? "the specified chain", ". Other tokens will not be converted."] }), _jsx("button", { onClick: handleCopyAddress, className: "anyspend-qr-copy-button flex w-full items-center justify-center gap-2 rounded-xl bg-blue-500 py-3.5 font-medium text-white transition-all hover:bg-blue-600", children: "Copy deposit address" })] }) }));
168
+ return (_jsx("div", { className: cn("anyspend-container anyspend-qr-deposit font-inter bg-as-surface-primary mx-auto w-full max-w-[460px] p-6", mode === "page" && "border-as-border-secondary overflow-hidden rounded-2xl border shadow-xl"), children: _jsxs("div", { className: "anyspend-qr-deposit-content flex flex-col gap-4", children: [_jsxs("div", { className: "anyspend-qr-header flex items-center justify-between", children: [_jsx("button", { onClick: handleBack, className: "anyspend-qr-back-button text-as-secondary hover:text-as-primary", children: _jsx("svg", { className: "h-5 w-5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M15 19l-7-7 7-7" }) }) }), _jsx("h2", { className: "anyspend-qr-title text-as-primary text-base font-semibold", children: "Deposit" }), onClose ? (_jsx("button", { onClick: handleClose, className: "anyspend-qr-close-button text-as-secondary hover:text-as-primary", children: _jsx("svg", { className: "h-5 w-5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) }) })) : (_jsx("div", { className: "w-5" }))] }), _jsxs("div", { className: "anyspend-qr-token-selector flex flex-col gap-1.5", children: [_jsx("label", { className: "anyspend-qr-token-label text-as-secondary text-sm", children: "Send" }), _jsx(TokenSelector, { chainIdsFilter: getAvailableChainIds("from"), context: "from", fromChainWalletVMSupported: true, isValidAddress: true, lockedChainIds: getAvailableChainIds("from"), multiWalletSupportEnabled: true, onAnalyticEvent: undefined, setToken: handleTokenSelect, supportedWalletVMs: ["evm"], token: undefined, trigger: _jsxs(Button, { variant: "outline", role: "combobox", className: "anyspend-qr-token-trigger border-as-stroke bg-as-surface-secondary flex h-auto w-full items-center justify-between gap-2 rounded-xl border px-3 py-2.5", children: [_jsxs("div", { className: "flex items-center gap-2", children: [sourceToken.metadata?.logoURI ? (_jsx(ChainTokenIcon, { chainUrl: ALL_CHAINS[sourceChainId]?.logoUrl, tokenUrl: sourceToken.metadata.logoURI, className: "h-8 min-h-8 w-8 min-w-8" })) : (_jsx("div", { className: "h-8 w-8 rounded-full bg-gray-700" })), _jsxs("div", { className: "flex flex-col items-start gap-0", children: [_jsx("div", { className: "text-as-primary font-semibold", children: sourceToken.symbol }), _jsx("div", { className: "text-as-primary/70 text-xs", children: ALL_CHAINS[sourceChainId]?.name ?? "Unknown" })] })] }), _jsx(ChevronsUpDown, { className: "h-4 w-4 shrink-0 opacity-70" })] }) })] }), _jsxs("div", { className: "anyspend-qr-content border-as-stroke flex items-start gap-4 rounded-xl border p-4", children: [_jsxs("div", { className: "anyspend-qr-code-container flex flex-col items-center gap-2", children: [_jsx("div", { className: "anyspend-qr-code rounded-lg bg-white p-2", children: _jsx(QRCodeSVG, { value: displayAddress, size: 120, level: "M", marginSize: 0 }) }), _jsxs("span", { className: "anyspend-qr-scan-hint text-as-secondary text-xs", children: ["SCAN WITH ", _jsx("span", { className: "inline-block", children: "\uD83E\uDD8A" })] })] }), _jsxs("div", { className: "anyspend-qr-address-container flex flex-1 flex-col gap-1", children: [_jsx("span", { className: "anyspend-qr-address-label text-as-secondary text-sm", children: "Deposit address:" }), _jsxs("div", { className: "anyspend-qr-address-row flex items-start gap-1", children: [_jsx("span", { className: "anyspend-qr-address text-as-primary break-all font-mono text-sm leading-relaxed", children: displayAddress }), _jsx("button", { onClick: handleCopyAddress, className: "anyspend-qr-copy-icon text-as-secondary hover:text-as-primary mt-0.5 shrink-0", children: copied ? _jsx(Check, { className: "h-4 w-4" }) : _jsx(Copy, { className: "h-4 w-4" }) })] })] })] }), _jsx(ChainWarningText, { chainId: destinationChainId }), _jsxs(WarningText, { children: ["Only send ", sourceToken.symbol, " on ", ALL_CHAINS[sourceChainId]?.name ?? "the specified chain", ". Other tokens will not be converted."] }), isPureTransfer && isWatchingTransfer && (_jsxs("div", { className: "anyspend-qr-watching flex items-center justify-center gap-2 rounded-lg bg-blue-500/10 p-3", children: [_jsx(Loader2, { className: "h-4 w-4 animate-spin text-blue-500" }), _jsx("span", { className: "text-sm text-blue-500", children: "Watching for incoming transfer..." })] })), _jsx("button", { onClick: handleCopyAddress, className: "anyspend-qr-copy-button flex w-full items-center justify-center gap-2 rounded-xl bg-blue-500 py-3.5 font-medium text-white transition-all hover:bg-blue-600", children: "Copy deposit address" })] }) }));
143
169
  }
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { toast, useAccountWallet, WalletImage } from "../../../../global-account/react/index.js";
3
+ import { toast, useAccountWallet, useModalStore, WalletImage } from "../../../../global-account/react/index.js";
4
4
  import { cn } from "../../../../shared/utils/cn.js";
5
5
  import { shortenAddress } from "../../../../shared/utils/formatAddress.js";
6
6
  import { client } from "../../../../shared/utils/thirdweb.js";
@@ -29,12 +29,16 @@ export function CryptoPaymentMethod({ selectedPaymentMethod, setSelectedPaymentM
29
29
  const globalAddress = connectedSmartWallet?.getAccount()?.address;
30
30
  // Use custom hook to determine wallet display logic
31
31
  const { shouldShowConnectedEOA } = useConnectedWalletDisplay(selectedPaymentMethod);
32
+ // Get modal store to block parent modal closing while connect modal is open
33
+ const setClosable = useModalStore(state => state.setClosable);
32
34
  // Handle wallet connection using thirdweb modal
33
35
  const handleConnectWallet = async () => {
36
+ // Block parent B3 modal from closing while thirdweb connect modal is open
37
+ setClosable(false);
34
38
  try {
35
39
  // Disconnect current wallet before connecting a new one
36
40
  if (connectedEOAWallet) {
37
- await disconnect(connectedEOAWallet);
41
+ disconnect(connectedEOAWallet);
38
42
  }
39
43
  const wallet = await openConnectModal({
40
44
  client,
@@ -44,7 +48,6 @@ export function CryptoPaymentMethod({ selectedPaymentMethod, setSelectedPaymentM
44
48
  wallets: recommendWallets,
45
49
  });
46
50
  if (wallet) {
47
- // setActiveWallet(wallet);
48
51
  setSelectedPaymentMethod(CryptoPaymentMethodType.CONNECT_WALLET);
49
52
  onSelectPaymentMethod(CryptoPaymentMethodType.CONNECT_WALLET);
50
53
  toast.success("Wallet connected");
@@ -64,6 +67,10 @@ export function CryptoPaymentMethod({ selectedPaymentMethod, setSelectedPaymentM
64
67
  }
65
68
  }
66
69
  }
70
+ finally {
71
+ // Always re-enable parent modal closing when connect modal closes
72
+ setClosable(true);
73
+ }
67
74
  };
68
75
  return (_jsx("div", { className: "crypto-payment-method mx-auto h-fit w-[460px] max-w-full px-5 pb-5 pt-5 sm:px-0 sm:pt-5", children: _jsxs("div", { className: cn("relative flex flex-col gap-10"), children: [_jsx("button", { onClick: onBack, className: "text-as-quaternary hover:text-as-primary absolute flex h-8 w-8 items-center justify-center rounded-lg transition-colors", children: _jsx(ChevronLeft, { className: "h-6 w-6" }) }), _jsx("div", { className: "flex items-center justify-around gap-4", children: _jsx("div", { className: "flex-1 text-center", children: _jsx("h2", { className: "text-as-primary text-lg font-semibold", children: "Select a payment method" }) }) }), process.env.NODE_ENV === "development" && (_jsxs("div", { className: "rounded-lg border border-yellow-500/50 bg-yellow-50 p-3 dark:bg-yellow-950/20", children: [_jsx("p", { className: "mb-2 text-xs font-semibold text-yellow-800 dark:text-yellow-300", children: "\uD83E\uDDEA Toast Test (Dev Only)" }), _jsxs("div", { className: "flex flex-wrap gap-2", children: [_jsx("button", { onClick: () => toast.success("Success! Transaction completed"), className: "rounded bg-green-600 px-2 py-1 text-xs font-medium text-white hover:bg-green-700", children: "Success" }), _jsx("button", { onClick: () => toast.error("Error! Transaction failed"), className: "rounded bg-red-600 px-2 py-1 text-xs font-medium text-white hover:bg-red-700", children: "Error" }), _jsx("button", { onClick: () => toast.info("Info: Processing your request..."), className: "rounded bg-blue-600 px-2 py-1 text-xs font-medium text-white hover:bg-blue-700", children: "Info" }), _jsx("button", { onClick: () => toast.warning("Warning: Low balance detected"), className: "rounded bg-yellow-600 px-2 py-1 text-xs font-medium text-white hover:bg-yellow-700", children: "Warning" }), _jsx("button", { onClick: () => {
69
76
  toast.success("Multiple test 1");
@@ -3,4 +3,4 @@ export interface GasIndicatorProps {
3
3
  gasPrice: GasPriceData;
4
4
  className?: string;
5
5
  }
6
- export declare function GasIndicator({ gasPrice, className }: GasIndicatorProps): import("react/jsx-runtime").JSX.Element;
6
+ export declare function GasIndicator({ gasPrice, className }: GasIndicatorProps): import("react/jsx-runtime").JSX.Element | null;
@@ -1,22 +1,8 @@
1
1
  "use client";
2
- import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { cn } from "../../../../shared/utils/cn.js";
4
4
  import { motion } from "motion/react";
5
- const LEVEL_LABELS = {
6
- low: "Low",
7
- normal: "Normal",
8
- elevated: "Elevated",
9
- high: "High",
10
- spike: "Spike",
11
- };
12
- const LEVEL_STYLES = {
13
- low: "bg-green-500/20 text-green-500",
14
- normal: "bg-as-surface-tertiary text-as-secondary",
15
- elevated: "bg-yellow-500/20 text-yellow-600",
16
- high: "bg-orange-500/20 text-orange-500",
17
- spike: "bg-red-500/20 text-red-500",
18
- };
19
- function formatGasPrice(gweiString) {
5
+ function formatGwei(gweiString) {
20
6
  const gwei = parseFloat(gweiString);
21
7
  if (gwei < 0.001)
22
8
  return "<0.001";
@@ -27,5 +13,9 @@ function formatGasPrice(gweiString) {
27
13
  return gwei.toFixed(1);
28
14
  }
29
15
  export function GasIndicator({ gasPrice, className }) {
30
- return (_jsxs(motion.div, { initial: { opacity: 0, y: 10 }, animate: { opacity: 1, y: 0 }, transition: { duration: 0.2 }, className: cn("flex items-center justify-between rounded-lg px-3 py-2", gasPrice.isSpike ? "bg-yellow-500/10" : "bg-as-surface-secondary", className), children: [_jsx("div", { className: "flex items-center gap-2", children: _jsxs("span", { className: "text-as-secondary text-xs", children: ["Gas on ", gasPrice.chainName] }) }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: cn("rounded px-1.5 py-0.5 text-xs font-medium", LEVEL_STYLES[gasPrice.level]), children: LEVEL_LABELS[gasPrice.level] }), _jsxs("span", { className: "text-as-primary text-xs font-medium", children: [formatGasPrice(gasPrice.gasPriceGwei), " Gwei"] })] })] }));
16
+ // Only show when gas is high or spike
17
+ if (!["high", "spike"].includes(gasPrice.level)) {
18
+ return null;
19
+ }
20
+ return (_jsxs(motion.div, { initial: { opacity: 0, y: 10 }, animate: { opacity: 1, y: 0 }, transition: { duration: 0.2 }, className: cn("flex flex-col gap-1 rounded-lg bg-orange-500/10 px-3 py-2", className), children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("span", { className: "text-xs font-medium text-orange-500", children: "Gas is high - transaction may fail or cost more" }), _jsxs("span", { className: "text-xs font-medium text-orange-500", children: [formatGwei(gasPrice.gasPriceGwei), " Gwei"] })] }), _jsx("span", { className: "text-as-secondary text-xs", children: "Consider swapping later for better rates" })] }));
31
21
  }
@@ -1,13 +1,24 @@
1
1
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { getNativeRequired } from "../../../../anyspend/utils/chain.js";
3
3
  import { isNativeToken } from "../../../../anyspend/utils/token.js";
4
- import { useTokenBalanceDirect } from "../../../../global-account/react/index.js";
4
+ import { useSimTokenBalance } from "../../../../global-account/react/index.js";
5
+ import { formatTokenAmount } from "../../../../shared/utils/number.js";
6
+ import { useMemo } from "react";
5
7
  import { formatUnits } from "viem";
6
8
  export function TokenBalance({ token, walletAddress, onChangeInput, }) {
7
- const { rawBalance, formattedBalance, isLoading } = useTokenBalanceDirect({
8
- token,
9
- address: walletAddress,
10
- });
9
+ const tokenAddress = isNativeToken(token.address) ? "native" : token.address;
10
+ const { data, isLoading } = useSimTokenBalance(walletAddress, tokenAddress, token.chainId);
11
+ const { rawBalance, formattedBalance } = useMemo(() => {
12
+ const balance = data?.balances?.[0];
13
+ if (!balance?.amount) {
14
+ return { rawBalance: null, formattedBalance: "0" };
15
+ }
16
+ const raw = BigInt(balance.amount);
17
+ return {
18
+ rawBalance: raw,
19
+ formattedBalance: formatTokenAmount(raw, balance.decimals),
20
+ };
21
+ }, [data]);
11
22
  const handlePercentageClick = (percentage) => {
12
23
  if (!rawBalance)
13
24
  return;
@@ -0,0 +1,22 @@
1
+ import { components } from "../../../../anyspend/types/api";
2
+ import { TransferResult } from "../../hooks/useWatchTransfer";
3
+ export interface TransferResultScreenProps {
4
+ mode?: "modal" | "page";
5
+ /** The transfer result containing amount info */
6
+ transferResult: TransferResult;
7
+ /** The token that was transferred */
8
+ token: components["schemas"]["Token"];
9
+ /** The chain ID where the transfer happened */
10
+ chainId: number;
11
+ /** The recipient address */
12
+ recipientAddress: string;
13
+ /** Callback when back/close button is clicked */
14
+ onBack?: () => void;
15
+ /** Callback when close button is clicked */
16
+ onClose?: () => void;
17
+ }
18
+ /**
19
+ * A component for displaying the result of a pure transfer (same chain, same token).
20
+ * Shows the transferred amount with success styling.
21
+ */
22
+ export declare function TransferResultScreen({ mode, transferResult, token, chainId, recipientAddress, onBack, onClose, }: TransferResultScreenProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,22 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { ALL_CHAINS } from "../../../../anyspend/index.js";
4
+ import { cn } from "../../../../shared/utils/cn.js";
5
+ import { CheckCircle2, Home } from "lucide-react";
6
+ import { ChainTokenIcon } from "./ChainTokenIcon.js";
7
+ /**
8
+ * A component for displaying the result of a pure transfer (same chain, same token).
9
+ * Shows the transferred amount with success styling.
10
+ */
11
+ export function TransferResultScreen({ mode = "modal", transferResult, token, chainId, recipientAddress, onBack, onClose, }) {
12
+ const chain = ALL_CHAINS[chainId];
13
+ const handleClose = () => {
14
+ if (onClose) {
15
+ onClose();
16
+ }
17
+ else if (onBack) {
18
+ onBack();
19
+ }
20
+ };
21
+ return (_jsx("div", { className: cn("anyspend-container anyspend-transfer-result font-inter bg-as-surface-primary mx-auto w-full max-w-[460px] p-6", mode === "page" && "border-as-border-secondary overflow-hidden rounded-2xl border shadow-xl"), children: _jsxs("div", { className: "anyspend-transfer-result-content flex flex-col items-center gap-6", children: [_jsx("div", { className: "anyspend-transfer-success-icon bg-as-success-secondary flex h-16 w-16 items-center justify-center rounded-full", children: _jsx(CheckCircle2, { className: "text-as-content-icon-success h-10 w-10" }) }), _jsxs("div", { className: "anyspend-transfer-success-message flex flex-col items-center gap-2", children: [_jsx("h2", { className: "text-as-primary text-xl font-semibold", children: "Transfer Received!" }), _jsx("p", { className: "text-as-secondary text-center text-sm", children: "Your transfer has been successfully received." })] }), _jsx("div", { className: "anyspend-transfer-amount border-as-border-secondary bg-as-surface-secondary flex w-full flex-col items-center gap-3 rounded-xl border p-4", children: _jsxs("div", { className: "flex items-center gap-3", children: [_jsx(ChainTokenIcon, { chainUrl: chain?.logoUrl, tokenUrl: token.metadata?.logoURI, className: "h-10 min-h-10 w-10 min-w-10" }), _jsxs("div", { className: "flex flex-col", children: [_jsxs("span", { className: "text-as-primary text-2xl font-bold", children: [transferResult.formattedAmount, " ", token.symbol] }), _jsxs("span", { className: "text-as-secondary text-sm", children: ["on ", chain?.name ?? "Unknown Chain"] })] })] }) }), _jsxs("div", { className: "anyspend-transfer-recipient flex w-full flex-col gap-1", children: [_jsx("span", { className: "text-as-secondary text-xs", children: "Received at" }), _jsx("span", { className: "text-as-primary break-all font-mono text-sm", children: recipientAddress })] }), _jsx("button", { onClick: handleClose, className: "anyspend-transfer-close-button bg-as-brand flex w-full items-center justify-center gap-2 rounded-xl py-3.5 font-medium text-white transition-all hover:opacity-90", children: mode === "page" ? (_jsxs(_Fragment, { children: ["Return to Home ", _jsx(Home, { className: "ml-2 h-4 w-4" })] })) : ("Close") })] }) }));
22
+ }
@@ -15,3 +15,4 @@ export * from "./useRecipientAddressState";
15
15
  export * from "./useSigMint";
16
16
  export * from "./useStripeClientSecret";
17
17
  export * from "./useStripeSupport";
18
+ export * from "./useWatchTransfer";
@@ -15,3 +15,4 @@ export * from "./useRecipientAddressState.js";
15
15
  export * from "./useSigMint.js";
16
16
  export * from "./useStripeClientSecret.js";
17
17
  export * from "./useStripeSupport.js";
18
+ export * from "./useWatchTransfer.js";
@@ -0,0 +1,41 @@
1
+ export interface TransferResult {
2
+ amount: string;
3
+ formattedAmount: string;
4
+ txHash?: string;
5
+ timestamp: number;
6
+ }
7
+ export interface UseWatchTransferProps {
8
+ /** Address to watch for incoming transfers */
9
+ address: string;
10
+ /** Chain ID to watch on */
11
+ chainId: number;
12
+ /** Token address (use zero address for native token) */
13
+ tokenAddress: string;
14
+ /** Token decimals */
15
+ tokenDecimals: number;
16
+ /** Whether watching is enabled */
17
+ enabled?: boolean;
18
+ /** Callback when a transfer is detected */
19
+ onTransferDetected?: (result: TransferResult) => void;
20
+ }
21
+ /**
22
+ * Hook to watch for incoming transfers to an address by monitoring balance changes.
23
+ * When a transfer is detected (balance increases), it captures the amount and notifies.
24
+ */
25
+ export declare function useWatchTransfer({ address, chainId, tokenAddress, tokenDecimals, enabled, onTransferDetected, }: UseWatchTransferProps): {
26
+ /** Whether currently watching for transfers */
27
+ isWatching: boolean;
28
+ /** The detected transfer result, if any */
29
+ transferResult: TransferResult | null;
30
+ /** Whether a transfer has been detected */
31
+ hasTransfer: boolean;
32
+ /** Reset and start watching again */
33
+ reset: () => void;
34
+ /** Manually refetch balance */
35
+ refetchBalance: (options?: import("@tanstack/query-core").RefetchOptions) => Promise<import("@tanstack/query-core").QueryObserverResult<{
36
+ decimals: number;
37
+ formatted: string;
38
+ symbol: string;
39
+ value: bigint;
40
+ }, import("viem").GetBalanceErrorType>>;
41
+ };
@@ -0,0 +1,72 @@
1
+ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
2
+ import { formatUnits } from "viem";
3
+ import { useBalance } from "wagmi";
4
+ import { isNativeToken } from "../../utils/index.js";
5
+ /**
6
+ * Hook to watch for incoming transfers to an address by monitoring balance changes.
7
+ * When a transfer is detected (balance increases), it captures the amount and notifies.
8
+ */
9
+ export function useWatchTransfer({ address, chainId, tokenAddress, tokenDecimals, enabled = true, onTransferDetected, }) {
10
+ const [transferResult, setTransferResult] = useState(null);
11
+ const [isWatching, setIsWatching] = useState(false);
12
+ const initialBalanceRef = useRef(null);
13
+ const transferDetectedRef = useRef(false);
14
+ const isNative = isNativeToken(tokenAddress);
15
+ // Get current balance
16
+ const { data: balanceData, refetch: refetchBalance } = useBalance({
17
+ address: address,
18
+ chainId,
19
+ token: isNative ? undefined : tokenAddress,
20
+ query: {
21
+ enabled: enabled && !!address,
22
+ refetchInterval: 3000,
23
+ },
24
+ });
25
+ // Initialize or update the initial balance
26
+ useEffect(() => {
27
+ if (balanceData && initialBalanceRef.current === null && enabled) {
28
+ initialBalanceRef.current = balanceData.value;
29
+ setIsWatching(true);
30
+ }
31
+ }, [balanceData, enabled]);
32
+ // Check for balance increase (transfer detected)
33
+ useEffect(() => {
34
+ if (!enabled || transferDetectedRef.current || initialBalanceRef.current === null || !balanceData) {
35
+ return;
36
+ }
37
+ const currentBalance = balanceData.value;
38
+ const initialBalance = initialBalanceRef.current;
39
+ if (currentBalance > initialBalance) {
40
+ const transferAmount = currentBalance - initialBalance;
41
+ const formattedAmount = formatUnits(transferAmount, tokenDecimals);
42
+ const result = {
43
+ amount: transferAmount.toString(),
44
+ formattedAmount,
45
+ timestamp: Date.now(),
46
+ };
47
+ transferDetectedRef.current = true;
48
+ setTransferResult(result);
49
+ setIsWatching(false);
50
+ onTransferDetected?.(result);
51
+ }
52
+ }, [balanceData, enabled, tokenDecimals, onTransferDetected]);
53
+ // Reset function to start watching again
54
+ const reset = useCallback(() => {
55
+ transferDetectedRef.current = false;
56
+ initialBalanceRef.current = null;
57
+ setTransferResult(null);
58
+ setIsWatching(false);
59
+ }, []);
60
+ return useMemo(() => ({
61
+ /** Whether currently watching for transfers */
62
+ isWatching,
63
+ /** The detected transfer result, if any */
64
+ transferResult,
65
+ /** Whether a transfer has been detected */
66
+ hasTransfer: transferResult !== null,
67
+ /** Reset and start watching again */
68
+ reset,
69
+ /** Manually refetch balance */
70
+ refetchBalance,
71
+ }), [isWatching, transferResult, reset, refetchBalance]);
72
+ }
@@ -1,6 +1,5 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { TooltipProvider } from "../../../global-account/react/index.js";
4
3
  import { FeatureFlagsProvider } from "../contexts/FeatureFlagsContext.js";
5
4
  import { StripeRedirectHandler } from "./StripeRedirectHandler.js";
6
5
  /**
@@ -27,5 +26,5 @@ import { StripeRedirectHandler } from "./StripeRedirectHandler.js";
27
26
  * ```
28
27
  */
29
28
  export const AnyspendProvider = function AnyspendProvider({ children, featureFlags }) {
30
- return (_jsx(FeatureFlagsProvider, { featureFlags: featureFlags, children: _jsxs(TooltipProvider, { children: [_jsx(StripeRedirectHandler, {}), children] }) }));
29
+ return (_jsxs(FeatureFlagsProvider, { featureFlags: featureFlags, children: [_jsx(StripeRedirectHandler, {}), children] }));
31
30
  };
@@ -13,3 +13,8 @@ export declare function isEvmOrSolanaAddress(address: string): boolean;
13
13
  export declare function isHyperliquidUSDC(chainId: number, address: string): boolean;
14
14
  export declare function normalizeAddress(address: string): string;
15
15
  export declare function eqci(a: string | null | undefined, b: string | null | undefined): boolean;
16
+ /**
17
+ * Check if source and destination represent the same token on the same chain.
18
+ * When true, this is a pure transfer (no swap/bridge needed).
19
+ */
20
+ export declare function isSameChainAndToken(sourceChainId: number, sourceTokenAddress: string, destinationChainId: number, destinationTokenAddress: string): boolean;
@@ -35,3 +35,10 @@ export function eqci(a, b) {
35
35
  return false;
36
36
  return a.toLowerCase() === b.toLowerCase();
37
37
  }
38
+ /**
39
+ * Check if source and destination represent the same token on the same chain.
40
+ * When true, this is a pure transfer (no swap/bridge needed).
41
+ */
42
+ export function isSameChainAndToken(sourceChainId, sourceTokenAddress, destinationChainId, destinationTokenAddress) {
43
+ return sourceChainId === destinationChainId && eqci(sourceTokenAddress, destinationTokenAddress);
44
+ }
@@ -18,6 +18,10 @@ export function AccountAssets({ nfts, isLoading }) {
18
18
  }, {});
19
19
  const collections = Object.values(groupedNFTs || {});
20
20
  const [expandedCollections, setExpandedCollections] = useState(() => new Set(collections.map(c => c.collection_id)));
21
+ const [failedImages, setFailedImages] = useState(() => new Set());
22
+ const handleImageError = (imageId) => {
23
+ setFailedImages(prev => new Set(prev).add(imageId));
24
+ };
21
25
  if (isLoading) {
22
26
  return (_jsx("div", { className: "flex flex-col gap-3", children: [...Array(2)].map((_, i) => (_jsxs("div", { className: "animate-pulse", children: [_jsx("div", { className: "bg-b3-react-muted mb-3 h-6 w-48 rounded" }), _jsxs("div", { className: "flex gap-3", children: [_jsx("div", { className: "bg-b3-react-muted h-[98px] w-[98px] shrink-0 rounded-lg" }), _jsx("div", { className: "bg-b3-react-muted h-[98px] w-[98px] shrink-0 rounded-lg" })] })] }, i))) }));
23
27
  }
@@ -36,11 +40,11 @@ export function AccountAssets({ nfts, isLoading }) {
36
40
  return next;
37
41
  });
38
42
  };
39
- return (_jsx("div", { className: "flex flex-col gap-3 px-4", children: collections.map(collection => {
43
+ return (_jsx("div", { className: "flex flex-col gap-3", children: collections.map(collection => {
40
44
  const isExpanded = expandedCollections.has(collection.collection_id);
41
- return (_jsxs("div", { className: "flex flex-col gap-3", children: [_jsxs("button", { onClick: () => toggleCollection(collection.collection_id), className: "flex w-full items-center justify-between", children: [_jsxs("div", { className: "flex items-center gap-1", children: [collection.collection_image && (_jsx("img", { src: collection.collection_image, alt: collection.collection_name, className: "h-5 w-5 shrink-0 rounded object-cover" })), _jsxs("p", { className: "font-neue-montreal-medium text-[14px] text-[#3f3f46]", children: [collection.collection_name, " (", collection.nfts.length, ")"] })] }), _jsx("svg", { className: `h-[18px] w-[18px] shrink-0 transition-transform ${isExpanded ? "rotate-180" : ""}`, viewBox: "0 0 18 18", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: _jsx("path", { d: "M4.5 6.75L9 11.25L13.5 6.75", stroke: "#51525C", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) })] }), isExpanded && (_jsx("div", { className: "flex gap-3 overflow-x-auto", children: collection.nfts.map(nft => (_jsx("div", { className: "relative h-[98px] w-[98px] shrink-0 overflow-hidden rounded-lg", children: _jsx("img", { src: nft.previews?.image_medium_url ||
45
+ return (_jsxs("div", { className: "flex flex-col gap-3", children: [_jsxs("button", { onClick: () => toggleCollection(collection.collection_id), className: "flex w-full items-center justify-between", children: [_jsxs("div", { className: "flex items-center gap-1", children: [collection.collection_image && !failedImages.has(`collection-${collection.collection_id}`) && (_jsx("img", { src: collection.collection_image, alt: collection.collection_name, className: "h-5 w-5 shrink-0 rounded object-cover", onError: () => handleImageError(`collection-${collection.collection_id}`) })), _jsxs("p", { className: "font-neue-montreal-medium text-[14px] text-[#3f3f46]", children: [collection.collection_name, " (", collection.nfts.length, ")"] })] }), _jsx("svg", { className: `h-[18px] w-[18px] shrink-0 transition-transform ${isExpanded ? "rotate-180" : ""}`, viewBox: "0 0 18 18", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: _jsx("path", { d: "M4.5 6.75L9 11.25L13.5 6.75", stroke: "#51525C", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) })] }), isExpanded && (_jsx("div", { className: "flex gap-3 overflow-x-auto", children: collection.nfts.map(nft => (_jsx("div", { className: "bg-b3-react-muted relative h-[98px] w-[98px] shrink-0 overflow-hidden rounded-lg", children: !failedImages.has(nft.nft_id) && (_jsx("img", { src: nft.previews?.image_medium_url ||
42
46
  nft.extra_metadata?.image_original_url ||
43
47
  nft.collection?.image_url ||
44
- "", alt: nft.name || "NFT", className: "h-full w-full object-cover" }) }, nft.nft_id))) }))] }, collection.collection_id));
48
+ "", alt: nft.name || "NFT", className: "h-full w-full object-cover", onError: () => handleImageError(nft.nft_id) })) }, nft.nft_id))) }))] }, collection.collection_id));
45
49
  }) }));
46
50
  }
@@ -1,15 +1,14 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { AnySpend, AnySpendBondKit, AnySpendBuySpin, AnySpendCollectorClubPurchase, AnySpendNFT, AnyspendSignatureMint, AnySpendStakeB3, AnySpendStakeB3ExactIn, AnySpendTournament, OrderHistory, } from "../../../anyspend/react/index.js";
3
+ import { AnySpendDeposit } from "../../../anyspend/react/components/AnySpendDeposit.js";
3
4
  import { AnySpendDepositHype } from "../../../anyspend/react/components/AnyspendDepositHype.js";
4
5
  import { AnySpendDepositUpside } from "../../../anyspend/react/components/AnySpendDepositUpside.js";
5
6
  import { AnySpendStakeUpside } from "../../../anyspend/react/components/AnySpendStakeUpside.js";
6
7
  import { AnySpendStakeUpsideExactIn } from "../../../anyspend/react/components/AnySpendStakeUpsideExactIn.js";
7
- import { useB3Config, useGlobalAccount, useIsMobile, useModalStore } from "../../../global-account/react/index.js";
8
+ import { useB3Config, useIsMobile, useModalStore } from "../../../global-account/react/index.js";
8
9
  import { cn } from "../../../shared/utils/cn.js";
9
10
  import { debugB3React } from "../../../shared/utils/debug.js";
10
11
  import { AnimatePresence, motion } from "framer-motion";
11
- import { useEffect, useRef } from "react";
12
- import { useSetActiveWallet } from "thirdweb/react";
13
12
  import { AvatarEditor } from "./AvatarEditor/AvatarEditor.js";
14
13
  import { Deposit } from "./Deposit/Deposit.js";
15
14
  import { LinkAccount } from "./LinkAccount/LinkAccount.js";
@@ -31,19 +30,7 @@ export function B3DynamicModal() {
31
30
  const navigateBack = useModalStore(state => state.navigateBack);
32
31
  const { theme } = useB3Config();
33
32
  const isMobile = useIsMobile();
34
- const prevIsOpenRef = useRef(isOpen);
35
- const { wallet } = useGlobalAccount();
36
- const setActiveWallet = useSetActiveWallet();
37
33
  const { toasts, removeToast } = useToastContext();
38
- // anyspend cleanup global account chnages by setting account back
39
- useEffect(() => {
40
- if (prevIsOpenRef.current && !isOpen) {
41
- if (wallet) {
42
- setActiveWallet(wallet);
43
- }
44
- }
45
- prevIsOpenRef.current = isOpen;
46
- }, [isOpen, wallet, setActiveWallet]);
47
34
  // Define arrays for different modal type groups
48
35
  const fullWidthTypes = [
49
36
  "anySpend",
@@ -66,6 +53,7 @@ export function B3DynamicModal() {
66
53
  "deposit",
67
54
  "send",
68
55
  "notifications",
56
+ "anySpendDeposit",
69
57
  ];
70
58
  const freestyleTypes = [
71
59
  "anySpendNft",
@@ -136,6 +124,8 @@ export function B3DynamicModal() {
136
124
  return _jsx(AnySpendDepositHype, { ...contentType, mode: "modal" });
137
125
  case "anySpendCollectorClubPurchase":
138
126
  return _jsx(AnySpendCollectorClubPurchase, { ...contentType, mode: "modal" });
127
+ case "anySpendDeposit":
128
+ return _jsx(AnySpendDeposit, { ...contentType, mode: "modal" });
139
129
  case "avatarEditor":
140
130
  return _jsx(AvatarEditor, { onSetAvatar: contentType.onSuccess });
141
131
  case "deposit":
@@ -0,0 +1,31 @@
1
+ import { CreateOnrampOrderParams } from "../../../../anyspend/react/hooks/useAnyspendCreateOnrampOrder";
2
+ import { CreateOrderParams } from "../../../../anyspend/react/hooks/useAnyspendCreateOrder";
3
+ import { PermissionsConfig } from "../../../../global-account/types/permissions";
4
+ import { Account } from "thirdweb/wallets";
5
+ import { ClientType } from "../../../client-manager";
6
+ export interface B3ConfigContextType {
7
+ accountOverride?: Account;
8
+ automaticallySetFirstEoa: boolean;
9
+ environment: "development" | "production";
10
+ defaultPermissions: PermissionsConfig;
11
+ theme: "light" | "dark";
12
+ clientType: ClientType;
13
+ partnerId: string;
14
+ stripePublishableKey?: string;
15
+ createClientReferenceId?: (params: CreateOrderParams | CreateOnrampOrderParams) => Promise<string>;
16
+ enableTurnkey: boolean;
17
+ }
18
+ export declare function B3ConfigProvider({ children, accountOverride, environment, defaultPermissions, automaticallySetFirstEoa, theme, clientType, partnerId, stripePublishableKey, createClientReferenceId, enableTurnkey, }: {
19
+ children: React.ReactNode;
20
+ accountOverride?: Account;
21
+ environment?: "development" | "production";
22
+ defaultPermissions?: PermissionsConfig;
23
+ automaticallySetFirstEoa?: boolean;
24
+ theme?: "light" | "dark";
25
+ clientType?: ClientType;
26
+ partnerId: string;
27
+ stripePublishableKey?: string;
28
+ createClientReferenceId?: (params: CreateOrderParams | CreateOnrampOrderParams) => Promise<string>;
29
+ enableTurnkey?: boolean;
30
+ }): import("react/jsx-runtime").JSX.Element;
31
+ export declare function useB3Config(): B3ConfigContextType;
@@ -0,0 +1,33 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { createContext, useContext } from "react";
3
+ /**
4
+ * Default permissions configuration for B3 provider
5
+ */
6
+ const DEFAULT_PERMISSIONS = {
7
+ approvedTargets: ["0xa8e42121e318e3D3BeD7f5969AF6D360045317DD"],
8
+ nativeTokenLimitPerTransaction: 0.1,
9
+ startDate: new Date(),
10
+ endDate: new Date(Date.now() + 1000 * 60 * 60 * 24 * 365), // 1 year from now
11
+ };
12
+ const B3ConfigContext = createContext(null);
13
+ export function B3ConfigProvider({ children, accountOverride, environment = "development", defaultPermissions = DEFAULT_PERMISSIONS, automaticallySetFirstEoa = false, theme = "light", clientType = "rest", partnerId, stripePublishableKey, createClientReferenceId, enableTurnkey = false, }) {
14
+ return (_jsx(B3ConfigContext.Provider, { value: {
15
+ accountOverride,
16
+ environment,
17
+ defaultPermissions,
18
+ automaticallySetFirstEoa,
19
+ theme,
20
+ clientType,
21
+ partnerId,
22
+ stripePublishableKey,
23
+ createClientReferenceId,
24
+ enableTurnkey,
25
+ }, children: children }));
26
+ }
27
+ export function useB3Config() {
28
+ const context = useContext(B3ConfigContext);
29
+ if (!context) {
30
+ throw new Error("useB3Config must be used within a B3ConfigProvider");
31
+ }
32
+ return context;
33
+ }