@b3dotfun/sdk 0.1.69-alpha.15 → 0.1.69-alpha.16

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.
@@ -205,5 +205,9 @@ variablePricing, feeOnTop, kycEnabled = false, callbackMetadata: callbackMetadat
205
205
  (shippingOptions && shippingOptions.length > 0) ||
206
206
  collectShippingAddress ||
207
207
  enableDiscountCode;
208
- return ((0, jsx_runtime_1.jsx)(AnySpendFingerprintWrapper_1.AnySpendFingerprintWrapper, { fingerprint: fingerprint, children: (0, jsx_runtime_1.jsx)(AnySpendCustomizationContext_1.AnySpendCustomizationProvider, { slots: slots, content: content, theme: theme, children: (0, jsx_runtime_1.jsx)(CheckoutLayout_1.CheckoutLayout, { mode: mode, paymentPanel: (0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [isVariablePricingActive && tokenData && variablePricing && ((0, jsx_runtime_1.jsx)(VariablePricingInput_1.VariablePricingInput, { config: variablePricing, tokenDecimals: tokenDecimals, tokenSymbol: tokenSymbol, themeColor: themeColor, onChange: setVariablePricingAmount })), hasFormContent && ((0, jsx_runtime_1.jsxs)("div", { className: "mb-6", children: [(0, jsx_runtime_1.jsx)(CheckoutFormPanel_1.CheckoutFormPanel, { formSchema: formSchema, formComponent: formComponent, shippingOptions: shippingOptions, collectShippingAddress: collectShippingAddress, enableDiscountCode: enableDiscountCode, validateDiscount: validateDiscount, tokenSymbol: tokenSymbol, tokenDecimals: tokenDecimals, classes: classes, formData: formData, onFormDataChange: handleFormDataChange, selectedShipping: selectedShipping, onShippingChange: handleShippingChange, appliedDiscount: appliedDiscount, onDiscountApplied: handleDiscountApplied, onDiscountRemoved: handleDiscountRemoved, shippingAddress: shippingAddress, onShippingAddressChange: setShippingAddress, checkoutFormSlot: slots?.checkoutForm }), (0, jsx_runtime_1.jsx)("div", { className: "mt-6 border-t border-gray-200 dark:border-neutral-700" })] })), (0, jsx_runtime_1.jsx)(CheckoutPaymentPanel_1.CheckoutPaymentPanel, { recipientAddress: recipientAddress, destinationTokenAddress: destinationTokenAddress, destinationTokenChainId: destinationTokenChainId, totalAmount: computedTotal, buttonText: buttonText, themeColor: themeColor, returnUrl: returnUrl, returnLabel: returnLabel, onSuccess: onSuccess, onError: onError, classes: classes, defaultPaymentMethod: defaultPaymentMethod, senderAddress: senderAddress, showPoints: showPoints, showOrderId: showOrderId, callbackMetadata: checkoutFormMetadata, isFormValid: isFormValid, feeOnTop: feeOnTop, kycEnabled: kycEnabled })] }), cartPanel: (0, jsx_runtime_1.jsx)(CheckoutCartPanel_1.CheckoutCartPanel, { items: items, totalAmount: computedTotal, tokenSymbol: tokenSymbol, tokenDecimals: tokenDecimals, organizationName: organizationName, organizationLogo: organizationLogo, classes: classes, footer: footer, shipping: effectiveShipping, tax: typeof tax === "string" ? { amount: tax } : tax, discount: effectiveDiscount, summaryLines: summaryLines, usdEquivalent: usdEquivalent }), classes: classes }) }) }));
208
+ return ((0, jsx_runtime_1.jsx)(AnySpendFingerprintWrapper_1.AnySpendFingerprintWrapper, { fingerprint: fingerprint, children: (0, jsx_runtime_1.jsx)(AnySpendCustomizationContext_1.AnySpendCustomizationProvider, { slots: slots, content: {
209
+ successTitle: "Payment Successful",
210
+ successDescription: "Your payment has been processed successfully.",
211
+ ...content,
212
+ }, theme: theme, children: (0, jsx_runtime_1.jsx)(CheckoutLayout_1.CheckoutLayout, { mode: mode, paymentPanel: (0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [isVariablePricingActive && tokenData && variablePricing && ((0, jsx_runtime_1.jsx)(VariablePricingInput_1.VariablePricingInput, { config: variablePricing, tokenDecimals: tokenDecimals, tokenSymbol: tokenSymbol, themeColor: themeColor, onChange: setVariablePricingAmount })), hasFormContent && ((0, jsx_runtime_1.jsxs)("div", { className: "mb-6", children: [(0, jsx_runtime_1.jsx)(CheckoutFormPanel_1.CheckoutFormPanel, { formSchema: formSchema, formComponent: formComponent, shippingOptions: shippingOptions, collectShippingAddress: collectShippingAddress, enableDiscountCode: enableDiscountCode, validateDiscount: validateDiscount, tokenSymbol: tokenSymbol, tokenDecimals: tokenDecimals, classes: classes, formData: formData, onFormDataChange: handleFormDataChange, selectedShipping: selectedShipping, onShippingChange: handleShippingChange, appliedDiscount: appliedDiscount, onDiscountApplied: handleDiscountApplied, onDiscountRemoved: handleDiscountRemoved, shippingAddress: shippingAddress, onShippingAddressChange: setShippingAddress, checkoutFormSlot: slots?.checkoutForm }), (0, jsx_runtime_1.jsx)("div", { className: "mt-6 border-t border-gray-200 dark:border-neutral-700" })] })), (0, jsx_runtime_1.jsx)(CheckoutPaymentPanel_1.CheckoutPaymentPanel, { recipientAddress: recipientAddress, destinationTokenAddress: destinationTokenAddress, destinationTokenChainId: destinationTokenChainId, totalAmount: computedTotal, buttonText: buttonText, themeColor: themeColor, returnUrl: returnUrl, returnLabel: returnLabel, onSuccess: onSuccess, onError: onError, classes: classes, defaultPaymentMethod: defaultPaymentMethod, senderAddress: senderAddress, showPoints: showPoints, showOrderId: showOrderId, callbackMetadata: checkoutFormMetadata, isFormValid: isFormValid, feeOnTop: feeOnTop, kycEnabled: kycEnabled })] }), cartPanel: (0, jsx_runtime_1.jsx)(CheckoutCartPanel_1.CheckoutCartPanel, { items: items, totalAmount: computedTotal, tokenSymbol: tokenSymbol, tokenDecimals: tokenDecimals, organizationName: organizationName, organizationLogo: organizationLogo, classes: classes, footer: footer, shipping: effectiveShipping, tax: typeof tax === "string" ? { amount: tax } : tax, discount: effectiveDiscount, summaryLines: summaryLines, usdEquivalent: usdEquivalent }), classes: classes }) }) }));
209
213
  }
@@ -13,7 +13,6 @@ const chain_1 = require("../../../../anyspend/utils/chain");
13
13
  const token_1 = require("../../../../anyspend/utils/token");
14
14
  const react_1 = require("../../../../global-account/react");
15
15
  const react_2 = require("../../../../global-account/react");
16
- const b3Chain_1 = require("../../../../shared/constants/chains/b3Chain");
17
16
  const number_1 = require("../../../../shared/utils/number");
18
17
  const cn_1 = require("../../../../shared/utils/cn");
19
18
  const lucide_react_1 = require("lucide-react");
@@ -21,8 +20,10 @@ const qrcode_react_1 = require("qrcode.react");
21
20
  const viem_1 = require("viem");
22
21
  const react_3 = require("motion/react");
23
22
  const react_4 = require("react");
23
+ const react_5 = require("thirdweb/react");
24
24
  const relay_kit_ui_1 = require("@relayprotocol/relay-kit-ui");
25
25
  const ChainTokenIcon_1 = require("../common/ChainTokenIcon");
26
+ const CryptoPaymentMethod_1 = require("../common/CryptoPaymentMethod");
26
27
  function CryptoPayPanel({ recipientAddress, destinationTokenAddress, destinationTokenChainId, totalAmount, buttonText = "Pay", themeColor, onSuccess, onOrderCreated, onError, callbackMetadata, classes, senderAddress, }) {
27
28
  // Stable refs for callback props to avoid re-triggering effects
28
29
  const onErrorRef = (0, react_4.useRef)(onError);
@@ -35,11 +36,10 @@ function CryptoPayPanel({ recipientAddress, destinationTokenAddress, destination
35
36
  const [selectedSrcChainId, setSelectedSrcChainId] = (0, react_4.useState)(destinationTokenChainId);
36
37
  const [selectedSrcToken, setSelectedSrcToken] = (0, react_4.useState)(null);
37
38
  const [copied, setCopied] = (0, react_4.useState)(false);
38
- const { address: walletAddress } = (0, react_1.useAccountWallet)();
39
- const effectiveAddress = senderAddress || walletAddress;
40
- const { partnerId } = (0, react_1.useB3Config)();
41
- const setB3ModalOpen = (0, react_1.useModalStore)(state => state.setB3ModalOpen);
42
- const setB3ModalContentType = (0, react_1.useModalStore)(state => state.setB3ModalContentType);
39
+ const { address: walletAddress, connectedEOAWallet } = (0, react_1.useAccountWallet)();
40
+ const connectedAddress = walletAddress || connectedEOAWallet?.getAccount()?.address;
41
+ const effectiveAddress = connectedAddress || senderAddress;
42
+ const { connect: openConnectModal } = (0, react_5.useConnectModal)();
43
43
  const { data: dstTokenData } = (0, react_1.useTokenData)(destinationTokenChainId, destinationTokenAddress);
44
44
  // Default to destination token data once available
45
45
  (0, react_4.useEffect)(() => {
@@ -169,9 +169,8 @@ function CryptoPayPanel({ recipientAddress, destinationTokenAddress, destination
169
169
  /* ------------------------------------------------------------------ */
170
170
  const [walletOrderId, setWalletOrderId] = (0, react_4.useState)();
171
171
  const [isSendingDeposit, setIsSendingDeposit] = (0, react_4.useState)(false);
172
- const [depositRejected, setDepositRejected] = (0, react_4.useState)(false);
173
172
  const depositSentRef = (0, react_4.useRef)(false);
174
- const { switchChainAndExecute } = (0, react_1.useUnifiedChainSwitchAndExecute)();
173
+ const { switchChainAndExecuteWithEOA } = (0, react_1.useUnifiedChainSwitchAndExecute)();
175
174
  const { createOrder: createSwapOrder, isCreatingOrder: isCreatingSwapOrder } = (0, useAnyspendCreateOrder_1.useAnyspendCreateOrder)({
176
175
  onSuccess: data => {
177
176
  const id = data.data?.id;
@@ -186,7 +185,7 @@ function CryptoPayPanel({ recipientAddress, destinationTokenAddress, destination
186
185
  const { orderAndTransactions: walletOat } = (0, useAnyspendOrderAndTransactions_1.useAnyspendOrderAndTransactions)(walletOrderId);
187
186
  // Auto-send deposit tx once swap order is ready
188
187
  (0, react_4.useEffect)(() => {
189
- if (!walletOat?.data?.order || depositSentRef.current || depositRejected)
188
+ if (!walletOat?.data?.order || depositSentRef.current)
190
189
  return;
191
190
  const order = walletOat.data.order;
192
191
  if (order.status !== "scanning_deposit_transaction")
@@ -198,8 +197,9 @@ function CryptoPayPanel({ recipientAddress, destinationTokenAddress, destination
198
197
  try {
199
198
  setIsSendingDeposit(true);
200
199
  const amount = BigInt(order.srcAmount);
200
+ let txHash;
201
201
  if ((0, token_1.isNativeToken)(order.srcTokenAddress)) {
202
- await switchChainAndExecute(order.srcChain, {
202
+ txHash = await switchChainAndExecuteWithEOA(order.srcChain, {
203
203
  to: order.globalAddress,
204
204
  value: amount,
205
205
  });
@@ -210,12 +210,18 @@ function CryptoPayPanel({ recipientAddress, destinationTokenAddress, destination
210
210
  functionName: "transfer",
211
211
  args: [order.globalAddress, amount],
212
212
  });
213
- await switchChainAndExecute(order.srcChain, {
213
+ txHash = await switchChainAndExecuteWithEOA(order.srcChain, {
214
214
  to: order.srcTokenAddress,
215
215
  data,
216
216
  value: BigInt(0),
217
217
  });
218
218
  }
219
+ if (!txHash) {
220
+ // User cancelled or tx failed — reset so they can retry
221
+ depositSentRef.current = false;
222
+ setWalletOrderId(undefined);
223
+ return;
224
+ }
219
225
  // Deposit sent — notify parent to transition to order lifecycle tracking
220
226
  if (walletOrderId) {
221
227
  onOrderCreatedRef.current?.(walletOrderId);
@@ -223,10 +229,8 @@ function CryptoPayPanel({ recipientAddress, destinationTokenAddress, destination
223
229
  }
224
230
  catch (error) {
225
231
  depositSentRef.current = false;
226
- const isUserRejection = error?.code === 4001 || error?.message?.includes("rejected") || error?.message?.includes("denied");
227
- if (isUserRejection) {
228
- setDepositRejected(true);
229
- }
232
+ // Reset order so user can retry
233
+ setWalletOrderId(undefined);
230
234
  onErrorRef.current?.(error instanceof Error ? error : new Error(error?.message || "Transaction rejected"));
231
235
  }
232
236
  finally {
@@ -234,7 +238,7 @@ function CryptoPayPanel({ recipientAddress, destinationTokenAddress, destination
234
238
  }
235
239
  };
236
240
  sendDeposit();
237
- }, [walletOat, switchChainAndExecute, walletOrderId, depositRejected]);
241
+ }, [walletOat, switchChainAndExecuteWithEOA, walletOrderId]);
238
242
  (0, useOnOrderSuccess_1.useOnOrderSuccess)({
239
243
  orderData: walletOat,
240
244
  orderId: walletOrderId,
@@ -242,9 +246,10 @@ function CryptoPayPanel({ recipientAddress, destinationTokenAddress, destination
242
246
  });
243
247
  const isWaitingForExecution = !!walletOrderId && walletOat?.data?.order.status !== "executed";
244
248
  const handleWalletPay = (0, react_4.useCallback)(() => {
245
- if (!selectedSrcToken || !walletAddress)
249
+ if (!selectedSrcToken || !connectedAddress)
246
250
  return;
247
251
  depositSentRef.current = false;
252
+ setWalletOrderId(undefined);
248
253
  createSwapOrder({
249
254
  recipientAddress,
250
255
  orderType: "swap",
@@ -259,7 +264,7 @@ function CryptoPayPanel({ recipientAddress, destinationTokenAddress, destination
259
264
  });
260
265
  }, [
261
266
  selectedSrcToken,
262
- walletAddress,
267
+ connectedAddress,
263
268
  effectiveAddress,
264
269
  recipientAddress,
265
270
  selectedSrcChainId,
@@ -291,13 +296,28 @@ function CryptoPayPanel({ recipientAddress, destinationTokenAddress, destination
291
296
  setTimeout(() => setCopied(false), 2000);
292
297
  }
293
298
  };
294
- const handleConnectWallet = () => {
295
- setB3ModalContentType({ type: "signInWithB3", showBackButton: false, chain: b3Chain_1.thirdwebB3Chain, partnerId });
296
- setB3ModalOpen(true);
299
+ const [shouldAutoPay, setShouldAutoPay] = (0, react_4.useState)(false);
300
+ const handleConnectWallet = async () => {
301
+ try {
302
+ const wallet = await openConnectModal(CryptoPaymentMethod_1.connectModalConfig);
303
+ if (wallet) {
304
+ setShouldAutoPay(true);
305
+ }
306
+ }
307
+ catch (error) {
308
+ console.error("Failed to connect wallet:", error);
309
+ }
297
310
  };
311
+ // Auto-trigger payment after wallet connect
312
+ (0, react_4.useEffect)(() => {
313
+ if (shouldAutoPay && connectedAddress && selectedSrcToken && hasEnoughBalance && !isLoadingAnyspendQuote) {
314
+ setShouldAutoPay(false);
315
+ handleWalletPay();
316
+ }
317
+ }, [shouldAutoPay, connectedAddress, selectedSrcToken, hasEnoughBalance, isLoadingAnyspendQuote, handleWalletPay]);
298
318
  const isLoading = isLoadingAnyspendQuote;
299
319
  const isPending = isCreatingSwapOrder || isSendingDeposit || isWaitingForExecution;
300
- const canPay = walletAddress && selectedSrcToken && hasEnoughBalance && !isLoading && !isPending;
320
+ const canPay = connectedAddress && selectedSrcToken && hasEnoughBalance && !isLoading && !isPending;
301
321
  const chainName = anyspend_1.ALL_CHAINS[selectedSrcChainId]?.name || "the specified chain";
302
322
  const chainLogoUrl = anyspend_1.ALL_CHAINS[selectedSrcChainId]?.logoUrl;
303
323
  // Collapse QR on mobile when a wallet connector is available
@@ -316,7 +336,7 @@ function CryptoPayPanel({ recipientAddress, destinationTokenAddress, destination
316
336
  name: token.name,
317
337
  symbol: token.symbol,
318
338
  });
319
- }, supportedWalletVMs: ["evm", "svm"], token: undefined, trigger: (0, jsx_runtime_1.jsxs)("button", { className: (0, cn_1.cn)("flex w-full items-center justify-between rounded-xl border border-gray-200 bg-white px-4 py-3 transition-colors hover:border-gray-300 dark:border-neutral-700 dark:bg-neutral-800 dark:hover:border-neutral-600", classes?.tokenSelector), children: [selectedSrcToken ? ((0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-3", children: [(0, jsx_runtime_1.jsx)(ChainTokenIcon_1.ChainTokenIcon, { chainUrl: anyspend_1.ALL_CHAINS[selectedSrcToken.chainId]?.logoUrl || "", tokenUrl: selectedSrcToken.metadata?.logoURI, className: "h-8 w-8" }), (0, jsx_runtime_1.jsxs)("div", { className: "text-left", children: [(0, jsx_runtime_1.jsx)("p", { className: "text-sm font-medium text-gray-900 dark:text-gray-100", children: selectedSrcToken.symbol }), (0, jsx_runtime_1.jsxs)("p", { className: "text-xs text-gray-500 dark:text-gray-400", children: ["Balance: ", balance.formatted] })] })] })) : ((0, jsx_runtime_1.jsx)("span", { className: "text-sm text-gray-400", children: "Select token" })), (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronsUpDown, { className: "h-4 w-4 text-gray-400" })] }) })] }), (0, jsx_runtime_1.jsx)(react_3.motion.div, { initial: { opacity: 0, y: 6 }, animate: { opacity: 1, y: 0 }, transition: { duration: 0.25, ease: "easeOut" }, className: (0, cn_1.cn)("rounded-xl border border-gray-200 bg-gray-50 px-4 py-3 dark:border-neutral-700 dark:bg-neutral-800/50", classes?.quoteDisplay), children: (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-between", children: [(0, jsx_runtime_1.jsx)("span", { className: "text-sm text-gray-500 dark:text-gray-400", children: "You pay" }), (0, jsx_runtime_1.jsx)(react_3.AnimatePresence, { mode: "wait", children: isLoadingAnyspendQuote ? ((0, jsx_runtime_1.jsx)(react_3.motion.div, { initial: { opacity: 0 }, animate: { opacity: 1 }, exit: { opacity: 0 }, transition: { duration: 0.15 }, children: (0, jsx_runtime_1.jsx)(react_2.TextShimmer, { duration: 1, className: "text-sm", children: "Fetching quote..." }) }, "quote-loading")) : ((0, jsx_runtime_1.jsxs)(react_3.motion.span, { initial: { opacity: 0 }, animate: { opacity: 1 }, exit: { opacity: 0 }, transition: { duration: 0.15 }, className: "text-sm font-medium text-gray-900 dark:text-gray-100", children: [srcAmountFormatted, " ", selectedSrcToken?.symbol || ""] }, "quote-amount")) })] }) }), (0, jsx_runtime_1.jsx)(react_3.AnimatePresence, { children: walletAddress && selectedSrcToken && !hasEnoughBalance && !isLoading && ((0, jsx_runtime_1.jsxs)(react_3.motion.p, { initial: { opacity: 0, height: 0 }, animate: { opacity: 1, height: "auto" }, exit: { opacity: 0, height: 0 }, transition: { duration: 0.2, ease: "easeOut" }, className: "text-center text-sm text-red-500", children: ["Insufficient ", selectedSrcToken.symbol, " balance"] }, "balance-warning")) }), !walletAddress ? ((0, jsx_runtime_1.jsx)(react_2.ShinyButton, { accentColor: themeColor || "hsl(var(--as-brand))", onClick: handleConnectWallet, className: (0, cn_1.cn)("w-full", classes?.payButton), textClassName: "text-white", children: "Connect Wallet to Pay" })) : ((0, jsx_runtime_1.jsx)(react_2.ShinyButton, { accentColor: themeColor || "hsl(var(--as-brand))", onClick: handleWalletPay, disabled: !canPay, className: (0, cn_1.cn)("w-full", classes?.payButton), textClassName: (0, cn_1.cn)(!canPay ? "text-as-secondary" : "text-white"), children: isPending ? ((0, jsx_runtime_1.jsxs)("span", { className: "flex items-center justify-center gap-2", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Loader2, { className: "h-4 w-4 animate-spin" }), isCreatingSwapOrder
339
+ }, supportedWalletVMs: ["evm", "svm"], token: undefined, trigger: (0, jsx_runtime_1.jsxs)("button", { className: (0, cn_1.cn)("flex w-full items-center justify-between rounded-xl border border-gray-200 bg-white px-4 py-3 transition-colors hover:border-gray-300 dark:border-neutral-700 dark:bg-neutral-800 dark:hover:border-neutral-600", classes?.tokenSelector), children: [selectedSrcToken ? ((0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-3", children: [(0, jsx_runtime_1.jsx)(ChainTokenIcon_1.ChainTokenIcon, { chainUrl: anyspend_1.ALL_CHAINS[selectedSrcToken.chainId]?.logoUrl || "", tokenUrl: selectedSrcToken.metadata?.logoURI, className: "h-8 w-8" }), (0, jsx_runtime_1.jsxs)("div", { className: "text-left", children: [(0, jsx_runtime_1.jsx)("p", { className: "text-sm font-medium text-gray-900 dark:text-gray-100", children: selectedSrcToken.symbol }), (0, jsx_runtime_1.jsxs)("p", { className: "text-xs text-gray-500 dark:text-gray-400", children: ["Balance: ", balance.formatted] })] })] })) : ((0, jsx_runtime_1.jsx)("span", { className: "text-sm text-gray-400", children: "Select token" })), (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronsUpDown, { className: "h-4 w-4 text-gray-400" })] }) })] }), (0, jsx_runtime_1.jsx)(react_3.motion.div, { initial: { opacity: 0, y: 6 }, animate: { opacity: 1, y: 0 }, transition: { duration: 0.25, ease: "easeOut" }, className: (0, cn_1.cn)("rounded-xl border border-gray-200 bg-gray-50 px-4 py-3 dark:border-neutral-700 dark:bg-neutral-800/50", classes?.quoteDisplay), children: (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-between", children: [(0, jsx_runtime_1.jsx)("span", { className: "text-sm text-gray-500 dark:text-gray-400", children: "You pay" }), (0, jsx_runtime_1.jsx)(react_3.AnimatePresence, { mode: "wait", children: isLoadingAnyspendQuote ? ((0, jsx_runtime_1.jsx)(react_3.motion.div, { initial: { opacity: 0 }, animate: { opacity: 1 }, exit: { opacity: 0 }, transition: { duration: 0.15 }, children: (0, jsx_runtime_1.jsx)(react_2.TextShimmer, { duration: 1, className: "text-sm", children: "Fetching quote..." }) }, "quote-loading")) : ((0, jsx_runtime_1.jsxs)(react_3.motion.span, { initial: { opacity: 0 }, animate: { opacity: 1 }, exit: { opacity: 0 }, transition: { duration: 0.15 }, className: "text-sm font-medium text-gray-900 dark:text-gray-100", children: [srcAmountFormatted, " ", selectedSrcToken?.symbol || ""] }, "quote-amount")) })] }) }), (0, jsx_runtime_1.jsx)(react_3.AnimatePresence, { children: connectedAddress && selectedSrcToken && !hasEnoughBalance && !isLoading && ((0, jsx_runtime_1.jsxs)(react_3.motion.p, { initial: { opacity: 0, height: 0 }, animate: { opacity: 1, height: "auto" }, exit: { opacity: 0, height: 0 }, transition: { duration: 0.2, ease: "easeOut" }, className: "text-center text-sm text-red-500", children: ["Insufficient ", selectedSrcToken.symbol, " balance"] }, "balance-warning")) }), !connectedAddress ? ((0, jsx_runtime_1.jsx)(react_2.ShinyButton, { accentColor: themeColor || "hsl(var(--as-brand))", onClick: handleConnectWallet, className: (0, cn_1.cn)("w-full", classes?.payButton), textClassName: "text-white", children: "Connect Wallet to Pay" })) : ((0, jsx_runtime_1.jsx)(react_2.ShinyButton, { accentColor: themeColor || "hsl(var(--as-brand))", onClick: handleWalletPay, disabled: !canPay, className: (0, cn_1.cn)("w-full", classes?.payButton), textClassName: (0, cn_1.cn)(!canPay ? "text-as-secondary" : "text-white"), children: isPending ? ((0, jsx_runtime_1.jsxs)("span", { className: "flex items-center justify-center gap-2", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Loader2, { className: "h-4 w-4 animate-spin" }), isCreatingSwapOrder
320
340
  ? "Creating order..."
321
341
  : isSendingDeposit
322
342
  ? "Confirm in wallet..."
@@ -5,6 +5,14 @@ export declare enum CryptoPaymentMethodType {
5
5
  GLOBAL_WALLET = "global_wallet",
6
6
  TRANSFER_CRYPTO = "transfer_crypto"
7
7
  }
8
+ export declare const recommendWallets: (import("thirdweb/wallets").Wallet<"io.metamask"> | import("thirdweb/wallets").Wallet<"com.coinbase.wallet"> | import("thirdweb/wallets").Wallet<"me.rainbow"> | import("thirdweb/wallets").Wallet<"io.rabby">)[];
9
+ export declare const connectModalConfig: {
10
+ client: import("thirdweb").ThirdwebClient;
11
+ setActive: false;
12
+ size: "compact";
13
+ showThirdwebBranding: boolean;
14
+ wallets: (import("thirdweb/wallets").Wallet<"io.metamask"> | import("thirdweb/wallets").Wallet<"com.coinbase.wallet"> | import("thirdweb/wallets").Wallet<"me.rainbow"> | import("thirdweb/wallets").Wallet<"io.rabby">)[];
15
+ };
8
16
  interface CryptoPaymentMethodProps {
9
17
  selectedPaymentMethod: CryptoPaymentMethodType;
10
18
  setSelectedPaymentMethod: (method: CryptoPaymentMethodType) => void;
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  "use client";
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
- exports.CryptoPaymentMethodType = void 0;
4
+ exports.connectModalConfig = exports.recommendWallets = exports.CryptoPaymentMethodType = void 0;
5
5
  exports.CryptoPaymentMethod = CryptoPaymentMethod;
6
6
  const jsx_runtime_1 = require("react/jsx-runtime");
7
7
  const react_1 = require("../../../../global-account/react");
@@ -19,12 +19,19 @@ var CryptoPaymentMethodType;
19
19
  CryptoPaymentMethodType["GLOBAL_WALLET"] = "global_wallet";
20
20
  CryptoPaymentMethodType["TRANSFER_CRYPTO"] = "transfer_crypto";
21
21
  })(CryptoPaymentMethodType || (exports.CryptoPaymentMethodType = CryptoPaymentMethodType = {}));
22
- const recommendWallets = [
22
+ exports.recommendWallets = [
23
23
  (0, wallets_1.createWallet)("io.metamask"),
24
24
  (0, wallets_1.createWallet)("com.coinbase.wallet"),
25
25
  (0, wallets_1.createWallet)("me.rainbow"),
26
26
  (0, wallets_1.createWallet)("io.rabby"),
27
27
  ];
28
+ exports.connectModalConfig = {
29
+ client: thirdweb_1.client,
30
+ setActive: false,
31
+ size: "compact",
32
+ showThirdwebBranding: false,
33
+ wallets: exports.recommendWallets,
34
+ };
28
35
  function CryptoPaymentMethod({ selectedPaymentMethod, setSelectedPaymentMethod, isCreatingOrder, onBack, onSelectPaymentMethod, classes, }) {
29
36
  const { connectedEOAWallet, connectedSmartWallet } = (0, react_1.useAccountWallet)();
30
37
  const { disconnect } = (0, react_2.useDisconnect)();
@@ -44,13 +51,7 @@ function CryptoPaymentMethod({ selectedPaymentMethod, setSelectedPaymentMethod,
44
51
  if (connectedEOAWallet) {
45
52
  disconnect(connectedEOAWallet);
46
53
  }
47
- const wallet = await openConnectModal({
48
- client: thirdweb_1.client,
49
- setActive: false,
50
- size: "compact",
51
- showThirdwebBranding: false,
52
- wallets: recommendWallets,
53
- });
54
+ const wallet = await openConnectModal(exports.connectModalConfig);
54
55
  if (wallet) {
55
56
  setSelectedPaymentMethod(CryptoPaymentMethodType.CONNECT_WALLET);
56
57
  onSelectPaymentMethod(CryptoPaymentMethodType.CONNECT_WALLET);
@@ -202,5 +202,9 @@ variablePricing, feeOnTop, kycEnabled = false, callbackMetadata: callbackMetadat
202
202
  (shippingOptions && shippingOptions.length > 0) ||
203
203
  collectShippingAddress ||
204
204
  enableDiscountCode;
205
- return (_jsx(AnySpendFingerprintWrapper, { fingerprint: fingerprint, children: _jsx(AnySpendCustomizationProvider, { slots: slots, content: content, theme: theme, children: _jsx(CheckoutLayout, { mode: mode, paymentPanel: _jsxs(_Fragment, { children: [isVariablePricingActive && tokenData && variablePricing && (_jsx(VariablePricingInput, { config: variablePricing, tokenDecimals: tokenDecimals, tokenSymbol: tokenSymbol, themeColor: themeColor, onChange: setVariablePricingAmount })), hasFormContent && (_jsxs("div", { className: "mb-6", children: [_jsx(CheckoutFormPanel, { formSchema: formSchema, formComponent: formComponent, shippingOptions: shippingOptions, collectShippingAddress: collectShippingAddress, enableDiscountCode: enableDiscountCode, validateDiscount: validateDiscount, tokenSymbol: tokenSymbol, tokenDecimals: tokenDecimals, classes: classes, formData: formData, onFormDataChange: handleFormDataChange, selectedShipping: selectedShipping, onShippingChange: handleShippingChange, appliedDiscount: appliedDiscount, onDiscountApplied: handleDiscountApplied, onDiscountRemoved: handleDiscountRemoved, shippingAddress: shippingAddress, onShippingAddressChange: setShippingAddress, checkoutFormSlot: slots?.checkoutForm }), _jsx("div", { className: "mt-6 border-t border-gray-200 dark:border-neutral-700" })] })), _jsx(CheckoutPaymentPanel, { recipientAddress: recipientAddress, destinationTokenAddress: destinationTokenAddress, destinationTokenChainId: destinationTokenChainId, totalAmount: computedTotal, buttonText: buttonText, themeColor: themeColor, returnUrl: returnUrl, returnLabel: returnLabel, onSuccess: onSuccess, onError: onError, classes: classes, defaultPaymentMethod: defaultPaymentMethod, senderAddress: senderAddress, showPoints: showPoints, showOrderId: showOrderId, callbackMetadata: checkoutFormMetadata, isFormValid: isFormValid, feeOnTop: feeOnTop, kycEnabled: kycEnabled })] }), cartPanel: _jsx(CheckoutCartPanel, { items: items, totalAmount: computedTotal, tokenSymbol: tokenSymbol, tokenDecimals: tokenDecimals, organizationName: organizationName, organizationLogo: organizationLogo, classes: classes, footer: footer, shipping: effectiveShipping, tax: typeof tax === "string" ? { amount: tax } : tax, discount: effectiveDiscount, summaryLines: summaryLines, usdEquivalent: usdEquivalent }), classes: classes }) }) }));
205
+ return (_jsx(AnySpendFingerprintWrapper, { fingerprint: fingerprint, children: _jsx(AnySpendCustomizationProvider, { slots: slots, content: {
206
+ successTitle: "Payment Successful",
207
+ successDescription: "Your payment has been processed successfully.",
208
+ ...content,
209
+ }, theme: theme, children: _jsx(CheckoutLayout, { mode: mode, paymentPanel: _jsxs(_Fragment, { children: [isVariablePricingActive && tokenData && variablePricing && (_jsx(VariablePricingInput, { config: variablePricing, tokenDecimals: tokenDecimals, tokenSymbol: tokenSymbol, themeColor: themeColor, onChange: setVariablePricingAmount })), hasFormContent && (_jsxs("div", { className: "mb-6", children: [_jsx(CheckoutFormPanel, { formSchema: formSchema, formComponent: formComponent, shippingOptions: shippingOptions, collectShippingAddress: collectShippingAddress, enableDiscountCode: enableDiscountCode, validateDiscount: validateDiscount, tokenSymbol: tokenSymbol, tokenDecimals: tokenDecimals, classes: classes, formData: formData, onFormDataChange: handleFormDataChange, selectedShipping: selectedShipping, onShippingChange: handleShippingChange, appliedDiscount: appliedDiscount, onDiscountApplied: handleDiscountApplied, onDiscountRemoved: handleDiscountRemoved, shippingAddress: shippingAddress, onShippingAddressChange: setShippingAddress, checkoutFormSlot: slots?.checkoutForm }), _jsx("div", { className: "mt-6 border-t border-gray-200 dark:border-neutral-700" })] })), _jsx(CheckoutPaymentPanel, { recipientAddress: recipientAddress, destinationTokenAddress: destinationTokenAddress, destinationTokenChainId: destinationTokenChainId, totalAmount: computedTotal, buttonText: buttonText, themeColor: themeColor, returnUrl: returnUrl, returnLabel: returnLabel, onSuccess: onSuccess, onError: onError, classes: classes, defaultPaymentMethod: defaultPaymentMethod, senderAddress: senderAddress, showPoints: showPoints, showOrderId: showOrderId, callbackMetadata: checkoutFormMetadata, isFormValid: isFormValid, feeOnTop: feeOnTop, kycEnabled: kycEnabled })] }), cartPanel: _jsx(CheckoutCartPanel, { items: items, totalAmount: computedTotal, tokenSymbol: tokenSymbol, tokenDecimals: tokenDecimals, organizationName: organizationName, organizationLogo: organizationLogo, classes: classes, footer: footer, shipping: effectiveShipping, tax: typeof tax === "string" ? { amount: tax } : tax, discount: effectiveDiscount, summaryLines: summaryLines, usdEquivalent: usdEquivalent }), classes: classes }) }) }));
206
210
  }
@@ -8,9 +8,8 @@ import { useOnOrderSuccess } from "../../../../anyspend/react/hooks/useOnOrderSu
8
8
  import { ALL_CHAINS, RELAY_SOLANA_MAINNET_CHAIN_ID, getAvailableChainIds } from "../../../../anyspend/index.js";
9
9
  import { getPaymentUrl } from "../../../../anyspend/utils/chain.js";
10
10
  import { isNativeToken } from "../../../../anyspend/utils/token.js";
11
- import { useAccountWallet, useB3Config, useIsMobile, useModalStore, useSimTokenBalance, useTokenData, useUnifiedChainSwitchAndExecute, } from "../../../../global-account/react/index.js";
11
+ import { useAccountWallet, useIsMobile, useSimTokenBalance, useTokenData, useUnifiedChainSwitchAndExecute, } from "../../../../global-account/react/index.js";
12
12
  import { ShinyButton, TextShimmer } from "../../../../global-account/react/index.js";
13
- import { thirdwebB3Chain } from "../../../../shared/constants/chains/b3Chain.js";
14
13
  import { formatTokenAmount } from "../../../../shared/utils/number.js";
15
14
  import { cn } from "../../../../shared/utils/cn.js";
16
15
  import { Check, ChevronDown, ChevronsUpDown, Copy, Loader2, QrCode } from "lucide-react";
@@ -18,8 +17,10 @@ import { QRCodeSVG } from "qrcode.react";
18
17
  import { encodeFunctionData, erc20Abi } from "viem";
19
18
  import { AnimatePresence, motion } from "motion/react";
20
19
  import { useCallback, useEffect, useMemo, useRef, useState } from "react";
20
+ import { useConnectModal } from "thirdweb/react";
21
21
  import { TokenSelector } from "@relayprotocol/relay-kit-ui";
22
22
  import { ChainTokenIcon } from "../common/ChainTokenIcon.js";
23
+ import { connectModalConfig } from "../common/CryptoPaymentMethod.js";
23
24
  export function CryptoPayPanel({ recipientAddress, destinationTokenAddress, destinationTokenChainId, totalAmount, buttonText = "Pay", themeColor, onSuccess, onOrderCreated, onError, callbackMetadata, classes, senderAddress, }) {
24
25
  // Stable refs for callback props to avoid re-triggering effects
25
26
  const onErrorRef = useRef(onError);
@@ -32,11 +33,10 @@ export function CryptoPayPanel({ recipientAddress, destinationTokenAddress, dest
32
33
  const [selectedSrcChainId, setSelectedSrcChainId] = useState(destinationTokenChainId);
33
34
  const [selectedSrcToken, setSelectedSrcToken] = useState(null);
34
35
  const [copied, setCopied] = useState(false);
35
- const { address: walletAddress } = useAccountWallet();
36
- const effectiveAddress = senderAddress || walletAddress;
37
- const { partnerId } = useB3Config();
38
- const setB3ModalOpen = useModalStore(state => state.setB3ModalOpen);
39
- const setB3ModalContentType = useModalStore(state => state.setB3ModalContentType);
36
+ const { address: walletAddress, connectedEOAWallet } = useAccountWallet();
37
+ const connectedAddress = walletAddress || connectedEOAWallet?.getAccount()?.address;
38
+ const effectiveAddress = connectedAddress || senderAddress;
39
+ const { connect: openConnectModal } = useConnectModal();
40
40
  const { data: dstTokenData } = useTokenData(destinationTokenChainId, destinationTokenAddress);
41
41
  // Default to destination token data once available
42
42
  useEffect(() => {
@@ -166,9 +166,8 @@ export function CryptoPayPanel({ recipientAddress, destinationTokenAddress, dest
166
166
  /* ------------------------------------------------------------------ */
167
167
  const [walletOrderId, setWalletOrderId] = useState();
168
168
  const [isSendingDeposit, setIsSendingDeposit] = useState(false);
169
- const [depositRejected, setDepositRejected] = useState(false);
170
169
  const depositSentRef = useRef(false);
171
- const { switchChainAndExecute } = useUnifiedChainSwitchAndExecute();
170
+ const { switchChainAndExecuteWithEOA } = useUnifiedChainSwitchAndExecute();
172
171
  const { createOrder: createSwapOrder, isCreatingOrder: isCreatingSwapOrder } = useAnyspendCreateOrder({
173
172
  onSuccess: data => {
174
173
  const id = data.data?.id;
@@ -183,7 +182,7 @@ export function CryptoPayPanel({ recipientAddress, destinationTokenAddress, dest
183
182
  const { orderAndTransactions: walletOat } = useAnyspendOrderAndTransactions(walletOrderId);
184
183
  // Auto-send deposit tx once swap order is ready
185
184
  useEffect(() => {
186
- if (!walletOat?.data?.order || depositSentRef.current || depositRejected)
185
+ if (!walletOat?.data?.order || depositSentRef.current)
187
186
  return;
188
187
  const order = walletOat.data.order;
189
188
  if (order.status !== "scanning_deposit_transaction")
@@ -195,8 +194,9 @@ export function CryptoPayPanel({ recipientAddress, destinationTokenAddress, dest
195
194
  try {
196
195
  setIsSendingDeposit(true);
197
196
  const amount = BigInt(order.srcAmount);
197
+ let txHash;
198
198
  if (isNativeToken(order.srcTokenAddress)) {
199
- await switchChainAndExecute(order.srcChain, {
199
+ txHash = await switchChainAndExecuteWithEOA(order.srcChain, {
200
200
  to: order.globalAddress,
201
201
  value: amount,
202
202
  });
@@ -207,12 +207,18 @@ export function CryptoPayPanel({ recipientAddress, destinationTokenAddress, dest
207
207
  functionName: "transfer",
208
208
  args: [order.globalAddress, amount],
209
209
  });
210
- await switchChainAndExecute(order.srcChain, {
210
+ txHash = await switchChainAndExecuteWithEOA(order.srcChain, {
211
211
  to: order.srcTokenAddress,
212
212
  data,
213
213
  value: BigInt(0),
214
214
  });
215
215
  }
216
+ if (!txHash) {
217
+ // User cancelled or tx failed — reset so they can retry
218
+ depositSentRef.current = false;
219
+ setWalletOrderId(undefined);
220
+ return;
221
+ }
216
222
  // Deposit sent — notify parent to transition to order lifecycle tracking
217
223
  if (walletOrderId) {
218
224
  onOrderCreatedRef.current?.(walletOrderId);
@@ -220,10 +226,8 @@ export function CryptoPayPanel({ recipientAddress, destinationTokenAddress, dest
220
226
  }
221
227
  catch (error) {
222
228
  depositSentRef.current = false;
223
- const isUserRejection = error?.code === 4001 || error?.message?.includes("rejected") || error?.message?.includes("denied");
224
- if (isUserRejection) {
225
- setDepositRejected(true);
226
- }
229
+ // Reset order so user can retry
230
+ setWalletOrderId(undefined);
227
231
  onErrorRef.current?.(error instanceof Error ? error : new Error(error?.message || "Transaction rejected"));
228
232
  }
229
233
  finally {
@@ -231,7 +235,7 @@ export function CryptoPayPanel({ recipientAddress, destinationTokenAddress, dest
231
235
  }
232
236
  };
233
237
  sendDeposit();
234
- }, [walletOat, switchChainAndExecute, walletOrderId, depositRejected]);
238
+ }, [walletOat, switchChainAndExecuteWithEOA, walletOrderId]);
235
239
  useOnOrderSuccess({
236
240
  orderData: walletOat,
237
241
  orderId: walletOrderId,
@@ -239,9 +243,10 @@ export function CryptoPayPanel({ recipientAddress, destinationTokenAddress, dest
239
243
  });
240
244
  const isWaitingForExecution = !!walletOrderId && walletOat?.data?.order.status !== "executed";
241
245
  const handleWalletPay = useCallback(() => {
242
- if (!selectedSrcToken || !walletAddress)
246
+ if (!selectedSrcToken || !connectedAddress)
243
247
  return;
244
248
  depositSentRef.current = false;
249
+ setWalletOrderId(undefined);
245
250
  createSwapOrder({
246
251
  recipientAddress,
247
252
  orderType: "swap",
@@ -256,7 +261,7 @@ export function CryptoPayPanel({ recipientAddress, destinationTokenAddress, dest
256
261
  });
257
262
  }, [
258
263
  selectedSrcToken,
259
- walletAddress,
264
+ connectedAddress,
260
265
  effectiveAddress,
261
266
  recipientAddress,
262
267
  selectedSrcChainId,
@@ -288,13 +293,28 @@ export function CryptoPayPanel({ recipientAddress, destinationTokenAddress, dest
288
293
  setTimeout(() => setCopied(false), 2000);
289
294
  }
290
295
  };
291
- const handleConnectWallet = () => {
292
- setB3ModalContentType({ type: "signInWithB3", showBackButton: false, chain: thirdwebB3Chain, partnerId });
293
- setB3ModalOpen(true);
296
+ const [shouldAutoPay, setShouldAutoPay] = useState(false);
297
+ const handleConnectWallet = async () => {
298
+ try {
299
+ const wallet = await openConnectModal(connectModalConfig);
300
+ if (wallet) {
301
+ setShouldAutoPay(true);
302
+ }
303
+ }
304
+ catch (error) {
305
+ console.error("Failed to connect wallet:", error);
306
+ }
294
307
  };
308
+ // Auto-trigger payment after wallet connect
309
+ useEffect(() => {
310
+ if (shouldAutoPay && connectedAddress && selectedSrcToken && hasEnoughBalance && !isLoadingAnyspendQuote) {
311
+ setShouldAutoPay(false);
312
+ handleWalletPay();
313
+ }
314
+ }, [shouldAutoPay, connectedAddress, selectedSrcToken, hasEnoughBalance, isLoadingAnyspendQuote, handleWalletPay]);
295
315
  const isLoading = isLoadingAnyspendQuote;
296
316
  const isPending = isCreatingSwapOrder || isSendingDeposit || isWaitingForExecution;
297
- const canPay = walletAddress && selectedSrcToken && hasEnoughBalance && !isLoading && !isPending;
317
+ const canPay = connectedAddress && selectedSrcToken && hasEnoughBalance && !isLoading && !isPending;
298
318
  const chainName = ALL_CHAINS[selectedSrcChainId]?.name || "the specified chain";
299
319
  const chainLogoUrl = ALL_CHAINS[selectedSrcChainId]?.logoUrl;
300
320
  // Collapse QR on mobile when a wallet connector is available
@@ -313,7 +333,7 @@ export function CryptoPayPanel({ recipientAddress, destinationTokenAddress, dest
313
333
  name: token.name,
314
334
  symbol: token.symbol,
315
335
  });
316
- }, supportedWalletVMs: ["evm", "svm"], token: undefined, trigger: _jsxs("button", { className: cn("flex w-full items-center justify-between rounded-xl border border-gray-200 bg-white px-4 py-3 transition-colors hover:border-gray-300 dark:border-neutral-700 dark:bg-neutral-800 dark:hover:border-neutral-600", classes?.tokenSelector), children: [selectedSrcToken ? (_jsxs("div", { className: "flex items-center gap-3", children: [_jsx(ChainTokenIcon, { chainUrl: ALL_CHAINS[selectedSrcToken.chainId]?.logoUrl || "", tokenUrl: selectedSrcToken.metadata?.logoURI, className: "h-8 w-8" }), _jsxs("div", { className: "text-left", children: [_jsx("p", { className: "text-sm font-medium text-gray-900 dark:text-gray-100", children: selectedSrcToken.symbol }), _jsxs("p", { className: "text-xs text-gray-500 dark:text-gray-400", children: ["Balance: ", balance.formatted] })] })] })) : (_jsx("span", { className: "text-sm text-gray-400", children: "Select token" })), _jsx(ChevronsUpDown, { className: "h-4 w-4 text-gray-400" })] }) })] }), _jsx(motion.div, { initial: { opacity: 0, y: 6 }, animate: { opacity: 1, y: 0 }, transition: { duration: 0.25, ease: "easeOut" }, className: cn("rounded-xl border border-gray-200 bg-gray-50 px-4 py-3 dark:border-neutral-700 dark:bg-neutral-800/50", classes?.quoteDisplay), children: _jsxs("div", { className: "flex items-center justify-between", children: [_jsx("span", { className: "text-sm text-gray-500 dark:text-gray-400", children: "You pay" }), _jsx(AnimatePresence, { mode: "wait", children: isLoadingAnyspendQuote ? (_jsx(motion.div, { initial: { opacity: 0 }, animate: { opacity: 1 }, exit: { opacity: 0 }, transition: { duration: 0.15 }, children: _jsx(TextShimmer, { duration: 1, className: "text-sm", children: "Fetching quote..." }) }, "quote-loading")) : (_jsxs(motion.span, { initial: { opacity: 0 }, animate: { opacity: 1 }, exit: { opacity: 0 }, transition: { duration: 0.15 }, className: "text-sm font-medium text-gray-900 dark:text-gray-100", children: [srcAmountFormatted, " ", selectedSrcToken?.symbol || ""] }, "quote-amount")) })] }) }), _jsx(AnimatePresence, { children: walletAddress && selectedSrcToken && !hasEnoughBalance && !isLoading && (_jsxs(motion.p, { initial: { opacity: 0, height: 0 }, animate: { opacity: 1, height: "auto" }, exit: { opacity: 0, height: 0 }, transition: { duration: 0.2, ease: "easeOut" }, className: "text-center text-sm text-red-500", children: ["Insufficient ", selectedSrcToken.symbol, " balance"] }, "balance-warning")) }), !walletAddress ? (_jsx(ShinyButton, { accentColor: themeColor || "hsl(var(--as-brand))", onClick: handleConnectWallet, className: cn("w-full", classes?.payButton), textClassName: "text-white", children: "Connect Wallet to Pay" })) : (_jsx(ShinyButton, { accentColor: themeColor || "hsl(var(--as-brand))", onClick: handleWalletPay, disabled: !canPay, className: cn("w-full", classes?.payButton), textClassName: cn(!canPay ? "text-as-secondary" : "text-white"), children: isPending ? (_jsxs("span", { className: "flex items-center justify-center gap-2", children: [_jsx(Loader2, { className: "h-4 w-4 animate-spin" }), isCreatingSwapOrder
336
+ }, supportedWalletVMs: ["evm", "svm"], token: undefined, trigger: _jsxs("button", { className: cn("flex w-full items-center justify-between rounded-xl border border-gray-200 bg-white px-4 py-3 transition-colors hover:border-gray-300 dark:border-neutral-700 dark:bg-neutral-800 dark:hover:border-neutral-600", classes?.tokenSelector), children: [selectedSrcToken ? (_jsxs("div", { className: "flex items-center gap-3", children: [_jsx(ChainTokenIcon, { chainUrl: ALL_CHAINS[selectedSrcToken.chainId]?.logoUrl || "", tokenUrl: selectedSrcToken.metadata?.logoURI, className: "h-8 w-8" }), _jsxs("div", { className: "text-left", children: [_jsx("p", { className: "text-sm font-medium text-gray-900 dark:text-gray-100", children: selectedSrcToken.symbol }), _jsxs("p", { className: "text-xs text-gray-500 dark:text-gray-400", children: ["Balance: ", balance.formatted] })] })] })) : (_jsx("span", { className: "text-sm text-gray-400", children: "Select token" })), _jsx(ChevronsUpDown, { className: "h-4 w-4 text-gray-400" })] }) })] }), _jsx(motion.div, { initial: { opacity: 0, y: 6 }, animate: { opacity: 1, y: 0 }, transition: { duration: 0.25, ease: "easeOut" }, className: cn("rounded-xl border border-gray-200 bg-gray-50 px-4 py-3 dark:border-neutral-700 dark:bg-neutral-800/50", classes?.quoteDisplay), children: _jsxs("div", { className: "flex items-center justify-between", children: [_jsx("span", { className: "text-sm text-gray-500 dark:text-gray-400", children: "You pay" }), _jsx(AnimatePresence, { mode: "wait", children: isLoadingAnyspendQuote ? (_jsx(motion.div, { initial: { opacity: 0 }, animate: { opacity: 1 }, exit: { opacity: 0 }, transition: { duration: 0.15 }, children: _jsx(TextShimmer, { duration: 1, className: "text-sm", children: "Fetching quote..." }) }, "quote-loading")) : (_jsxs(motion.span, { initial: { opacity: 0 }, animate: { opacity: 1 }, exit: { opacity: 0 }, transition: { duration: 0.15 }, className: "text-sm font-medium text-gray-900 dark:text-gray-100", children: [srcAmountFormatted, " ", selectedSrcToken?.symbol || ""] }, "quote-amount")) })] }) }), _jsx(AnimatePresence, { children: connectedAddress && selectedSrcToken && !hasEnoughBalance && !isLoading && (_jsxs(motion.p, { initial: { opacity: 0, height: 0 }, animate: { opacity: 1, height: "auto" }, exit: { opacity: 0, height: 0 }, transition: { duration: 0.2, ease: "easeOut" }, className: "text-center text-sm text-red-500", children: ["Insufficient ", selectedSrcToken.symbol, " balance"] }, "balance-warning")) }), !connectedAddress ? (_jsx(ShinyButton, { accentColor: themeColor || "hsl(var(--as-brand))", onClick: handleConnectWallet, className: cn("w-full", classes?.payButton), textClassName: "text-white", children: "Connect Wallet to Pay" })) : (_jsx(ShinyButton, { accentColor: themeColor || "hsl(var(--as-brand))", onClick: handleWalletPay, disabled: !canPay, className: cn("w-full", classes?.payButton), textClassName: cn(!canPay ? "text-as-secondary" : "text-white"), children: isPending ? (_jsxs("span", { className: "flex items-center justify-center gap-2", children: [_jsx(Loader2, { className: "h-4 w-4 animate-spin" }), isCreatingSwapOrder
317
337
  ? "Creating order..."
318
338
  : isSendingDeposit
319
339
  ? "Confirm in wallet..."
@@ -5,6 +5,14 @@ export declare enum CryptoPaymentMethodType {
5
5
  GLOBAL_WALLET = "global_wallet",
6
6
  TRANSFER_CRYPTO = "transfer_crypto"
7
7
  }
8
+ export declare const recommendWallets: (import("thirdweb/wallets").Wallet<"io.metamask"> | import("thirdweb/wallets").Wallet<"com.coinbase.wallet"> | import("thirdweb/wallets").Wallet<"me.rainbow"> | import("thirdweb/wallets").Wallet<"io.rabby">)[];
9
+ export declare const connectModalConfig: {
10
+ client: import("thirdweb").ThirdwebClient;
11
+ setActive: false;
12
+ size: "compact";
13
+ showThirdwebBranding: boolean;
14
+ wallets: (import("thirdweb/wallets").Wallet<"io.metamask"> | import("thirdweb/wallets").Wallet<"com.coinbase.wallet"> | import("thirdweb/wallets").Wallet<"me.rainbow"> | import("thirdweb/wallets").Wallet<"io.rabby">)[];
15
+ };
8
16
  interface CryptoPaymentMethodProps {
9
17
  selectedPaymentMethod: CryptoPaymentMethodType;
10
18
  setSelectedPaymentMethod: (method: CryptoPaymentMethodType) => void;
@@ -15,12 +15,19 @@ export var CryptoPaymentMethodType;
15
15
  CryptoPaymentMethodType["GLOBAL_WALLET"] = "global_wallet";
16
16
  CryptoPaymentMethodType["TRANSFER_CRYPTO"] = "transfer_crypto";
17
17
  })(CryptoPaymentMethodType || (CryptoPaymentMethodType = {}));
18
- const recommendWallets = [
18
+ export const recommendWallets = [
19
19
  createWallet("io.metamask"),
20
20
  createWallet("com.coinbase.wallet"),
21
21
  createWallet("me.rainbow"),
22
22
  createWallet("io.rabby"),
23
23
  ];
24
+ export const connectModalConfig = {
25
+ client,
26
+ setActive: false,
27
+ size: "compact",
28
+ showThirdwebBranding: false,
29
+ wallets: recommendWallets,
30
+ };
24
31
  export function CryptoPaymentMethod({ selectedPaymentMethod, setSelectedPaymentMethod, isCreatingOrder, onBack, onSelectPaymentMethod, classes, }) {
25
32
  const { connectedEOAWallet, connectedSmartWallet } = useAccountWallet();
26
33
  const { disconnect } = useDisconnect();
@@ -40,13 +47,7 @@ export function CryptoPaymentMethod({ selectedPaymentMethod, setSelectedPaymentM
40
47
  if (connectedEOAWallet) {
41
48
  disconnect(connectedEOAWallet);
42
49
  }
43
- const wallet = await openConnectModal({
44
- client,
45
- setActive: false,
46
- size: "compact",
47
- showThirdwebBranding: false,
48
- wallets: recommendWallets,
49
- });
50
+ const wallet = await openConnectModal(connectModalConfig);
50
51
  if (wallet) {
51
52
  setSelectedPaymentMethod(CryptoPaymentMethodType.CONNECT_WALLET);
52
53
  onSelectPaymentMethod(CryptoPaymentMethodType.CONNECT_WALLET);
@@ -5,6 +5,14 @@ export declare enum CryptoPaymentMethodType {
5
5
  GLOBAL_WALLET = "global_wallet",
6
6
  TRANSFER_CRYPTO = "transfer_crypto"
7
7
  }
8
+ export declare const recommendWallets: (import("thirdweb/wallets").Wallet<"io.metamask"> | import("thirdweb/wallets").Wallet<"com.coinbase.wallet"> | import("thirdweb/wallets").Wallet<"me.rainbow"> | import("thirdweb/wallets").Wallet<"io.rabby">)[];
9
+ export declare const connectModalConfig: {
10
+ client: import("thirdweb").ThirdwebClient;
11
+ setActive: false;
12
+ size: "compact";
13
+ showThirdwebBranding: boolean;
14
+ wallets: (import("thirdweb/wallets").Wallet<"io.metamask"> | import("thirdweb/wallets").Wallet<"com.coinbase.wallet"> | import("thirdweb/wallets").Wallet<"me.rainbow"> | import("thirdweb/wallets").Wallet<"io.rabby">)[];
15
+ };
8
16
  interface CryptoPaymentMethodProps {
9
17
  selectedPaymentMethod: CryptoPaymentMethodType;
10
18
  setSelectedPaymentMethod: (method: CryptoPaymentMethodType) => void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@b3dotfun/sdk",
3
- "version": "0.1.69-alpha.15",
3
+ "version": "0.1.69-alpha.16",
4
4
  "source": "src/index.ts",
5
5
  "main": "./dist/cjs/index.js",
6
6
  "react-native": "./dist/cjs/index.native.js",
@@ -377,7 +377,15 @@ export function AnySpendCheckout({
377
377
 
378
378
  return (
379
379
  <AnySpendFingerprintWrapper fingerprint={fingerprint}>
380
- <AnySpendCustomizationProvider slots={slots} content={content} theme={theme}>
380
+ <AnySpendCustomizationProvider
381
+ slots={slots}
382
+ content={{
383
+ successTitle: "Payment Successful",
384
+ successDescription: "Your payment has been processed successfully.",
385
+ ...content,
386
+ }}
387
+ theme={theme}
388
+ >
381
389
  <CheckoutLayout
382
390
  mode={mode}
383
391
  paymentPanel={
@@ -11,15 +11,12 @@ import { getPaymentUrl } from "@b3dotfun/sdk/anyspend/utils/chain";
11
11
  import { isNativeToken } from "@b3dotfun/sdk/anyspend/utils/token";
12
12
  import {
13
13
  useAccountWallet,
14
- useB3Config,
15
14
  useIsMobile,
16
- useModalStore,
17
15
  useSimTokenBalance,
18
16
  useTokenData,
19
17
  useUnifiedChainSwitchAndExecute,
20
18
  } from "@b3dotfun/sdk/global-account/react";
21
19
  import { ShinyButton, TextShimmer } from "@b3dotfun/sdk/global-account/react";
22
- import { thirdwebB3Chain } from "@b3dotfun/sdk/shared/constants/chains/b3Chain";
23
20
  import { formatTokenAmount } from "@b3dotfun/sdk/shared/utils/number";
24
21
  import { cn } from "@b3dotfun/sdk/shared/utils/cn";
25
22
  import { Check, ChevronDown, ChevronsUpDown, Copy, Loader2, QrCode } from "lucide-react";
@@ -27,8 +24,10 @@ import { QRCodeSVG } from "qrcode.react";
27
24
  import { encodeFunctionData, erc20Abi } from "viem";
28
25
  import { AnimatePresence, motion } from "motion/react";
29
26
  import { useCallback, useEffect, useMemo, useRef, useState } from "react";
27
+ import { useConnectModal } from "thirdweb/react";
30
28
  import { TokenSelector } from "@relayprotocol/relay-kit-ui";
31
29
  import { ChainTokenIcon } from "../common/ChainTokenIcon";
30
+ import { connectModalConfig } from "../common/CryptoPaymentMethod";
32
31
  import type { AnySpendCheckoutClasses } from "./AnySpendCheckout";
33
32
 
34
33
  interface CryptoPayPanelProps {
@@ -76,11 +75,10 @@ export function CryptoPayPanel({
76
75
  const [selectedSrcToken, setSelectedSrcToken] = useState<components["schemas"]["Token"] | null>(null);
77
76
  const [copied, setCopied] = useState(false);
78
77
 
79
- const { address: walletAddress } = useAccountWallet();
80
- const effectiveAddress = senderAddress || walletAddress;
81
- const { partnerId } = useB3Config();
82
- const setB3ModalOpen = useModalStore(state => state.setB3ModalOpen);
83
- const setB3ModalContentType = useModalStore(state => state.setB3ModalContentType);
78
+ const { address: walletAddress, connectedEOAWallet } = useAccountWallet();
79
+ const connectedAddress = walletAddress || connectedEOAWallet?.getAccount()?.address;
80
+ const effectiveAddress = connectedAddress || senderAddress;
81
+ const { connect: openConnectModal } = useConnectModal();
84
82
 
85
83
  const { data: dstTokenData } = useTokenData(destinationTokenChainId, destinationTokenAddress);
86
84
 
@@ -235,9 +233,8 @@ export function CryptoPayPanel({
235
233
  /* ------------------------------------------------------------------ */
236
234
  const [walletOrderId, setWalletOrderId] = useState<string | undefined>();
237
235
  const [isSendingDeposit, setIsSendingDeposit] = useState(false);
238
- const [depositRejected, setDepositRejected] = useState(false);
239
236
  const depositSentRef = useRef(false);
240
- const { switchChainAndExecute } = useUnifiedChainSwitchAndExecute();
237
+ const { switchChainAndExecuteWithEOA } = useUnifiedChainSwitchAndExecute();
241
238
 
242
239
  const { createOrder: createSwapOrder, isCreatingOrder: isCreatingSwapOrder } = useAnyspendCreateOrder({
243
240
  onSuccess: data => {
@@ -254,7 +251,7 @@ export function CryptoPayPanel({
254
251
 
255
252
  // Auto-send deposit tx once swap order is ready
256
253
  useEffect(() => {
257
- if (!walletOat?.data?.order || depositSentRef.current || depositRejected) return;
254
+ if (!walletOat?.data?.order || depositSentRef.current) return;
258
255
  const order = walletOat.data.order;
259
256
  if (order.status !== "scanning_deposit_transaction") return;
260
257
  if (walletOat.data.depositTxs?.length) return;
@@ -264,8 +261,9 @@ export function CryptoPayPanel({
264
261
  try {
265
262
  setIsSendingDeposit(true);
266
263
  const amount = BigInt(order.srcAmount);
264
+ let txHash: string | undefined;
267
265
  if (isNativeToken(order.srcTokenAddress)) {
268
- await switchChainAndExecute(order.srcChain, {
266
+ txHash = await switchChainAndExecuteWithEOA(order.srcChain, {
269
267
  to: order.globalAddress as `0x${string}`,
270
268
  value: amount,
271
269
  });
@@ -275,30 +273,33 @@ export function CryptoPayPanel({
275
273
  functionName: "transfer",
276
274
  args: [order.globalAddress as `0x${string}`, amount],
277
275
  });
278
- await switchChainAndExecute(order.srcChain, {
276
+ txHash = await switchChainAndExecuteWithEOA(order.srcChain, {
279
277
  to: order.srcTokenAddress as `0x${string}`,
280
278
  data,
281
279
  value: BigInt(0),
282
280
  });
283
281
  }
282
+ if (!txHash) {
283
+ // User cancelled or tx failed — reset so they can retry
284
+ depositSentRef.current = false;
285
+ setWalletOrderId(undefined);
286
+ return;
287
+ }
284
288
  // Deposit sent — notify parent to transition to order lifecycle tracking
285
289
  if (walletOrderId) {
286
290
  onOrderCreatedRef.current?.(walletOrderId);
287
291
  }
288
292
  } catch (error: any) {
289
293
  depositSentRef.current = false;
290
- const isUserRejection =
291
- error?.code === 4001 || error?.message?.includes("rejected") || error?.message?.includes("denied");
292
- if (isUserRejection) {
293
- setDepositRejected(true);
294
- }
294
+ // Reset order so user can retry
295
+ setWalletOrderId(undefined);
295
296
  onErrorRef.current?.(error instanceof Error ? error : new Error(error?.message || "Transaction rejected"));
296
297
  } finally {
297
298
  setIsSendingDeposit(false);
298
299
  }
299
300
  };
300
301
  sendDeposit();
301
- }, [walletOat, switchChainAndExecute, walletOrderId, depositRejected]);
302
+ }, [walletOat, switchChainAndExecuteWithEOA, walletOrderId]);
302
303
 
303
304
  useOnOrderSuccess({
304
305
  orderData: walletOat,
@@ -309,8 +310,9 @@ export function CryptoPayPanel({
309
310
  const isWaitingForExecution = !!walletOrderId && walletOat?.data?.order.status !== "executed";
310
311
 
311
312
  const handleWalletPay = useCallback(() => {
312
- if (!selectedSrcToken || !walletAddress) return;
313
+ if (!selectedSrcToken || !connectedAddress) return;
313
314
  depositSentRef.current = false;
315
+ setWalletOrderId(undefined);
314
316
  createSwapOrder({
315
317
  recipientAddress,
316
318
  orderType: "swap",
@@ -325,7 +327,7 @@ export function CryptoPayPanel({
325
327
  });
326
328
  }, [
327
329
  selectedSrcToken,
328
- walletAddress,
330
+ connectedAddress,
329
331
  effectiveAddress,
330
332
  recipientAddress,
331
333
  selectedSrcChainId,
@@ -360,14 +362,30 @@ export function CryptoPayPanel({
360
362
  }
361
363
  };
362
364
 
363
- const handleConnectWallet = () => {
364
- setB3ModalContentType({ type: "signInWithB3", showBackButton: false, chain: thirdwebB3Chain, partnerId });
365
- setB3ModalOpen(true);
365
+ const [shouldAutoPay, setShouldAutoPay] = useState(false);
366
+
367
+ const handleConnectWallet = async () => {
368
+ try {
369
+ const wallet = await openConnectModal(connectModalConfig);
370
+ if (wallet) {
371
+ setShouldAutoPay(true);
372
+ }
373
+ } catch (error) {
374
+ console.error("Failed to connect wallet:", error);
375
+ }
366
376
  };
367
377
 
378
+ // Auto-trigger payment after wallet connect
379
+ useEffect(() => {
380
+ if (shouldAutoPay && connectedAddress && selectedSrcToken && hasEnoughBalance && !isLoadingAnyspendQuote) {
381
+ setShouldAutoPay(false);
382
+ handleWalletPay();
383
+ }
384
+ }, [shouldAutoPay, connectedAddress, selectedSrcToken, hasEnoughBalance, isLoadingAnyspendQuote, handleWalletPay]);
385
+
368
386
  const isLoading = isLoadingAnyspendQuote;
369
387
  const isPending = isCreatingSwapOrder || isSendingDeposit || isWaitingForExecution;
370
- const canPay = walletAddress && selectedSrcToken && hasEnoughBalance && !isLoading && !isPending;
388
+ const canPay = connectedAddress && selectedSrcToken && hasEnoughBalance && !isLoading && !isPending;
371
389
 
372
390
  const chainName = ALL_CHAINS[selectedSrcChainId]?.name || "the specified chain";
373
391
  const chainLogoUrl = ALL_CHAINS[selectedSrcChainId]?.logoUrl;
@@ -478,7 +496,7 @@ export function CryptoPayPanel({
478
496
 
479
497
  {/* ---- Insufficient balance warning ---- */}
480
498
  <AnimatePresence>
481
- {walletAddress && selectedSrcToken && !hasEnoughBalance && !isLoading && (
499
+ {connectedAddress && selectedSrcToken && !hasEnoughBalance && !isLoading && (
482
500
  <motion.p
483
501
  key="balance-warning"
484
502
  initial={{ opacity: 0, height: 0 }}
@@ -493,7 +511,7 @@ export function CryptoPayPanel({
493
511
  </AnimatePresence>
494
512
 
495
513
  {/* ---- Wallet Pay Button ---- */}
496
- {!walletAddress ? (
514
+ {!connectedAddress ? (
497
515
  <ShinyButton
498
516
  accentColor={themeColor || "hsl(var(--as-brand))"}
499
517
  onClick={handleConnectWallet}
@@ -17,13 +17,21 @@ export enum CryptoPaymentMethodType {
17
17
  TRANSFER_CRYPTO = "transfer_crypto",
18
18
  }
19
19
 
20
- const recommendWallets = [
20
+ export const recommendWallets = [
21
21
  createWallet("io.metamask"),
22
22
  createWallet("com.coinbase.wallet"),
23
23
  createWallet("me.rainbow"),
24
24
  createWallet("io.rabby"),
25
25
  ];
26
26
 
27
+ export const connectModalConfig = {
28
+ client,
29
+ setActive: false as const,
30
+ size: "compact" as const,
31
+ showThirdwebBranding: false,
32
+ wallets: recommendWallets,
33
+ };
34
+
27
35
  interface CryptoPaymentMethodProps {
28
36
  selectedPaymentMethod: CryptoPaymentMethodType;
29
37
  setSelectedPaymentMethod: (method: CryptoPaymentMethodType) => void;
@@ -65,13 +73,7 @@ export function CryptoPaymentMethod({
65
73
  disconnect(connectedEOAWallet);
66
74
  }
67
75
 
68
- const wallet = await openConnectModal({
69
- client,
70
- setActive: false,
71
- size: "compact",
72
- showThirdwebBranding: false,
73
- wallets: recommendWallets,
74
- });
76
+ const wallet = await openConnectModal(connectModalConfig);
75
77
 
76
78
  if (wallet) {
77
79
  setSelectedPaymentMethod(CryptoPaymentMethodType.CONNECT_WALLET);