@b3dotfun/sdk 0.1.69-alpha.3 → 0.1.69-alpha.5

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 (29) hide show
  1. package/dist/cjs/anyspend/react/components/checkout/CheckoutPaymentPanel.js +2 -4
  2. package/dist/cjs/anyspend/react/hooks/useAnyspendCreateOnrampOrder.js +4 -6
  3. package/dist/cjs/anyspend/react/hooks/useKycStatus.d.ts +3 -1
  4. package/dist/cjs/anyspend/react/hooks/useKycStatus.js +11 -7
  5. package/dist/cjs/global-account/react/components/SignInWithB3/components/AuthButton.js +10 -1
  6. package/dist/cjs/global-account/react/components/SignInWithB3/steps/LoginStepCustom.js +96 -15
  7. package/dist/cjs/global-account/react/components/SignInWithB3/utils/signInUtils.d.ts +5 -3
  8. package/dist/cjs/global-account/react/components/SignInWithB3/utils/signInUtils.js +15 -2
  9. package/dist/cjs/global-account/react/hooks/useConnect.d.ts +2 -2
  10. package/dist/esm/anyspend/react/components/checkout/CheckoutPaymentPanel.js +2 -4
  11. package/dist/esm/anyspend/react/hooks/useAnyspendCreateOnrampOrder.js +5 -7
  12. package/dist/esm/anyspend/react/hooks/useKycStatus.d.ts +3 -1
  13. package/dist/esm/anyspend/react/hooks/useKycStatus.js +9 -5
  14. package/dist/esm/global-account/react/components/SignInWithB3/components/AuthButton.js +11 -2
  15. package/dist/esm/global-account/react/components/SignInWithB3/steps/LoginStepCustom.js +100 -19
  16. package/dist/esm/global-account/react/components/SignInWithB3/utils/signInUtils.d.ts +5 -3
  17. package/dist/esm/global-account/react/components/SignInWithB3/utils/signInUtils.js +14 -1
  18. package/dist/esm/global-account/react/hooks/useConnect.d.ts +2 -2
  19. package/dist/types/anyspend/react/hooks/useKycStatus.d.ts +3 -1
  20. package/dist/types/global-account/react/components/SignInWithB3/utils/signInUtils.d.ts +5 -3
  21. package/dist/types/global-account/react/hooks/useConnect.d.ts +2 -2
  22. package/package.json +1 -1
  23. package/src/anyspend/react/components/checkout/CheckoutPaymentPanel.tsx +2 -4
  24. package/src/anyspend/react/hooks/useAnyspendCreateOnrampOrder.ts +5 -7
  25. package/src/anyspend/react/hooks/useKycStatus.ts +8 -5
  26. package/src/global-account/react/components/SignInWithB3/components/AuthButton.tsx +21 -2
  27. package/src/global-account/react/components/SignInWithB3/steps/LoginStepCustom.tsx +207 -54
  28. package/src/global-account/react/components/SignInWithB3/utils/signInUtils.ts +19 -3
  29. package/src/global-account/react/hooks/useConnect.tsx +2 -2
@@ -56,9 +56,7 @@ function CheckoutPaymentPanel({ recipientAddress, destinationTokenAddress, desti
56
56
  if (activeOrderId) {
57
57
  return ((0, jsx_runtime_1.jsx)(CheckoutOrderStatus_1.CheckoutOrderStatus, { orderId: activeOrderId, themeColor: themeColor, returnUrl: returnUrl, returnLabel: returnLabel, onSuccess: onSuccess, onError: onError, onRetry: handleRetry, showPoints: showPoints, showOrderId: showOrderId, classes: classes }));
58
58
  }
59
- const accordionButtonClass = (active) => (0, cn_1.cn)("anyspend-payment-method-btn flex w-full items-center gap-3 px-4 py-4 text-left transition-colors", active
60
- ? "bg-white dark:bg-neutral-900"
61
- : "bg-white hover:bg-gray-50 dark:bg-neutral-900 dark:hover:bg-neutral-800", classes?.paymentMethodButton);
62
- const expandedPanelClass = (0, cn_1.cn)("anyspend-payment-method-panel border-t border-gray-100 bg-white px-4 py-4 dark:border-neutral-800 dark:bg-neutral-900");
59
+ const accordionButtonClass = (active) => (0, cn_1.cn)("anyspend-payment-method-btn flex w-full items-center gap-3 px-4 py-4 text-left transition-colors", !active && "hover:bg-gray-50 dark:hover:bg-neutral-800", classes?.paymentMethodButton);
60
+ const expandedPanelClass = (0, cn_1.cn)("anyspend-payment-method-panel border-t border-gray-100 px-4 py-4 dark:border-neutral-800");
63
61
  return ((0, jsx_runtime_1.jsxs)("div", { className: (0, cn_1.cn)("anyspend-payment-panel flex flex-col gap-5", classes?.paymentPanel), children: [(0, jsx_runtime_1.jsx)("h2", { className: (0, cn_1.cn)("anyspend-payment-title text-lg font-semibold text-gray-900 dark:text-gray-100", classes?.paymentTitle), children: "Payment" }), !isFormValid && ((0, jsx_runtime_1.jsx)("p", { className: "text-sm text-amber-600 dark:text-amber-400", children: "Please complete the required fields above before proceeding to payment." })), (0, jsx_runtime_1.jsxs)("div", { className: (0, cn_1.cn)("anyspend-payment-methods divide-y divide-gray-200 overflow-hidden rounded-xl border border-gray-200 dark:divide-neutral-700 dark:border-neutral-700", !isFormValid && "pointer-events-none opacity-50", classes?.paymentMethodSelector), children: [(0, jsx_runtime_1.jsxs)("div", { className: "anyspend-method-crypto", children: [(0, jsx_runtime_1.jsxs)("button", { onClick: () => setPaymentMethod(paymentMethod === "crypto" ? null : "crypto"), className: accordionButtonClass(paymentMethod === "crypto"), children: [(0, jsx_runtime_1.jsx)(RadioCircle, { selected: paymentMethod === "crypto", themeColor: themeColor }), (0, jsx_runtime_1.jsx)(lucide_react_1.Wallet, { className: "h-5 w-5 text-gray-700 dark:text-gray-300" }), (0, jsx_runtime_1.jsx)("span", { className: "text-sm font-medium text-gray-900 dark:text-gray-100", children: "Pay with crypto" })] }), (0, jsx_runtime_1.jsx)(react_1.AnimatePresence, { initial: false, children: paymentMethod === "crypto" && ((0, jsx_runtime_1.jsx)(react_1.motion.div, { initial: { height: 0, opacity: 0 }, animate: { height: "auto", opacity: 1 }, exit: { height: 0, opacity: 0 }, transition: { duration: 0.2, ease: "easeOut" }, style: { overflow: "hidden" }, children: (0, jsx_runtime_1.jsx)("div", { className: expandedPanelClass, children: (0, jsx_runtime_1.jsx)(CryptoPayPanel_1.CryptoPayPanel, { recipientAddress: recipientAddress, destinationTokenAddress: destinationTokenAddress, destinationTokenChainId: destinationTokenChainId, totalAmount: totalAmount, buttonText: buttonText, themeColor: themeColor, onOrderCreated: handleOrderCreated, onError: onError, callbackMetadata: callbackMetadata, classes: classes, senderAddress: senderAddress }) }) }, "crypto-panel")) })] }), (0, jsx_runtime_1.jsxs)("div", { className: "anyspend-method-card", children: [(0, jsx_runtime_1.jsxs)("button", { onClick: () => setPaymentMethod(paymentMethod === "card" ? null : "card"), className: accordionButtonClass(paymentMethod === "card"), children: [(0, jsx_runtime_1.jsx)(RadioCircle, { selected: paymentMethod === "card", themeColor: themeColor }), (0, jsx_runtime_1.jsx)(lucide_react_1.CreditCard, { className: "h-5 w-5 text-gray-700 dark:text-gray-300" }), (0, jsx_runtime_1.jsx)("span", { className: "text-sm font-medium text-gray-900 dark:text-gray-100", children: "Credit or debit card" }), (0, jsx_runtime_1.jsxs)("div", { className: "ml-auto flex items-center gap-1", children: [(0, jsx_runtime_1.jsx)(VisaLogo, {}), (0, jsx_runtime_1.jsx)(MastercardLogo, {}), (0, jsx_runtime_1.jsx)(AmexLogo, {})] })] }), (0, jsx_runtime_1.jsx)(react_1.AnimatePresence, { initial: false, children: paymentMethod === "card" && ((0, jsx_runtime_1.jsx)(react_1.motion.div, { initial: { height: 0, opacity: 0 }, animate: { height: "auto", opacity: 1 }, exit: { height: 0, opacity: 0 }, transition: { duration: 0.2, ease: "easeOut" }, style: { overflow: "hidden" }, children: (0, jsx_runtime_1.jsx)("div", { className: expandedPanelClass, children: (0, jsx_runtime_1.jsx)(FiatCheckoutPanel_1.FiatCheckoutPanel, { recipientAddress: recipientAddress, destinationTokenAddress: destinationTokenAddress, destinationTokenChainId: destinationTokenChainId, totalAmount: totalAmount, themeColor: themeColor, onOrderCreated: handleOrderCreated, onError: onError, callbackMetadata: callbackMetadata, classes: classes, feeOnTop: feeOnTop, kycEnabled: kycEnabled }) }) }, "card-panel")) })] }), (0, jsx_runtime_1.jsxs)("div", { className: "anyspend-method-coinbase", children: [(0, jsx_runtime_1.jsxs)("button", { onClick: () => setPaymentMethod(paymentMethod === "coinbase" ? null : "coinbase"), className: accordionButtonClass(paymentMethod === "coinbase"), children: [(0, jsx_runtime_1.jsx)(RadioCircle, { selected: paymentMethod === "coinbase", themeColor: themeColor }), (0, jsx_runtime_1.jsx)(CoinbaseLogo, {}), (0, jsx_runtime_1.jsx)("span", { className: "text-sm font-medium text-gray-900 dark:text-gray-100", children: "Coinbase Pay" })] }), (0, jsx_runtime_1.jsx)(react_1.AnimatePresence, { initial: false, children: paymentMethod === "coinbase" && ((0, jsx_runtime_1.jsx)(react_1.motion.div, { initial: { height: 0, opacity: 0 }, animate: { height: "auto", opacity: 1 }, exit: { height: 0, opacity: 0 }, transition: { duration: 0.2, ease: "easeOut" }, style: { overflow: "hidden" }, children: (0, jsx_runtime_1.jsx)("div", { className: expandedPanelClass, children: (0, jsx_runtime_1.jsx)(CoinbaseCheckoutPanel_1.CoinbaseCheckoutPanel, { recipientAddress: recipientAddress, destinationTokenAddress: destinationTokenAddress, destinationTokenChainId: destinationTokenChainId, totalAmount: totalAmount, themeColor: themeColor, onOrderCreated: handleOrderCreated, onError: onError, callbackMetadata: callbackMetadata, classes: classes }) }) }, "coinbase-panel")) })] })] })] }));
64
62
  }
@@ -10,7 +10,6 @@ const react_query_1 = require("@tanstack/react-query");
10
10
  const react_2 = require("react");
11
11
  const viem_1 = require("viem");
12
12
  const chains_1 = require("viem/chains");
13
- const wagmi_1 = require("wagmi");
14
13
  const useKycStatus_1 = require("./useKycStatus");
15
14
  const useValidatedClientReferenceId_1 = require("./useValidatedClientReferenceId");
16
15
  /**
@@ -20,7 +19,7 @@ const useValidatedClientReferenceId_1 = require("./useValidatedClientReferenceId
20
19
  function useAnyspendCreateOnrampOrder({ onSuccess, onError } = {}) {
21
20
  // Get B3 context values
22
21
  const { partnerId } = (0, react_1.useB3Config)();
23
- const { address } = (0, wagmi_1.useAccount)();
22
+ const { address, getHeaders: getWalletAuthHeaders } = (0, useKycStatus_1.useWalletAuthHeaders)();
24
23
  // Get validated client reference ID from B3 context
25
24
  const createValidatedClientReferenceId = (0, useValidatedClientReferenceId_1.useValidatedClientReferenceId)();
26
25
  // Get fingerprint data
@@ -44,12 +43,11 @@ function useAnyspendCreateOnrampOrder({ onSuccess, onError } = {}) {
44
43
  const srcAmountOnRampInWei = (0, viem_1.parseUnits)(srcFiatAmount, constants_1.USDC_BASE.decimals);
45
44
  // For card payments, include wallet auth headers so the backend can verify
46
45
  // KYC by the signing wallet address (may differ from the B3 JWT address).
47
- // Only use already-cached headers never trigger a fresh wallet signature
48
- // here, as that would prompt the user without their explicit consent.
49
- // KycGate pre-caches the headers in the "Continue to Verify" user-gesture.
46
+ // First try cached headers (from KycGate), then sign on-the-fly.
47
+ // The user is already clicking "Continue"/"Pay" so signing here is acceptable.
50
48
  let kycWalletHeaders;
51
49
  if (onramp.vendor === "stripe-web2" && address) {
52
- kycWalletHeaders = (0, useKycStatus_1.getCachedWalletHeaders)(address);
50
+ kycWalletHeaders = (0, useKycStatus_1.getCachedWalletHeaders)(address) || (await getWalletAuthHeaders());
53
51
  }
54
52
  return await anyspend_1.anyspendService.createOrder({
55
53
  recipientAddress: (0, utils_1.normalizeAddress)(recipientAddress),
@@ -24,10 +24,12 @@ interface KycVerifyResponse {
24
24
  export declare function getCachedWalletHeaders(address: string): Record<string, string> | undefined;
25
25
  /**
26
26
  * Returns a function that builds the wallet-signature auth headers.
27
+ * Uses useAccountWallet (thirdweb) instead of wagmi so signing works
28
+ * for all wallet types (social login, EOA, smart wallet, etc.).
27
29
  * Caches signatures for 4 minutes (server allows 5-minute window).
28
30
  */
29
31
  export declare function useWalletAuthHeaders(): {
30
- address: `0x${string}` | undefined;
32
+ address: string | undefined;
31
33
  getHeaders: () => Promise<Record<string, string>>;
32
34
  };
33
35
  export declare function useKycStatus(enabled?: boolean): {
@@ -7,9 +7,9 @@ exports.useKycStatus = useKycStatus;
7
7
  exports.useCreateKycInquiry = useCreateKycInquiry;
8
8
  exports.useVerifyKyc = useVerifyKyc;
9
9
  const constants_1 = require("../../../anyspend/constants");
10
+ const react_1 = require("../../../global-account/react");
10
11
  const react_query_1 = require("@tanstack/react-query");
11
- const react_1 = require("react");
12
- const wagmi_1 = require("wagmi");
12
+ const react_2 = require("react");
13
13
  function buildWalletAuthMessage(walletAddress, timestamp) {
14
14
  return `AnySpend wants to verify your identity for card payments.\n\nThis signature does not trigger a blockchain transaction or cost any gas.\n\nWallet: ${walletAddress.toLowerCase()}\nNonce: ${timestamp}`;
15
15
  }
@@ -27,21 +27,25 @@ function getCachedWalletHeaders(address) {
27
27
  }
28
28
  /**
29
29
  * Returns a function that builds the wallet-signature auth headers.
30
+ * Uses useAccountWallet (thirdweb) instead of wagmi so signing works
31
+ * for all wallet types (social login, EOA, smart wallet, etc.).
30
32
  * Caches signatures for 4 minutes (server allows 5-minute window).
31
33
  */
32
34
  function useWalletAuthHeaders() {
33
- const { address } = (0, wagmi_1.useAccount)();
34
- const { signMessageAsync } = (0, wagmi_1.useSignMessage)();
35
- const getHeaders = (0, react_1.useCallback)(async () => {
35
+ const { address, wallet } = (0, react_1.useAccountWallet)();
36
+ const signMessage = wallet.signMessage;
37
+ const getHeaders = (0, react_2.useCallback)(async () => {
36
38
  if (!address)
37
39
  throw new Error("No wallet connected");
40
+ if (!signMessage)
41
+ throw new Error("Wallet does not support message signing");
38
42
  const walletAddress = address.toLowerCase();
39
43
  const cached = headerCache.get(walletAddress);
40
44
  if (cached && Date.now() < cached.expiresAt)
41
45
  return cached.headers;
42
46
  const timestamp = Math.floor(Date.now() / 1000);
43
47
  const message = buildWalletAuthMessage(walletAddress, timestamp);
44
- const signature = await signMessageAsync({ message });
48
+ const signature = await signMessage({ message });
45
49
  const headers = {
46
50
  "X-Wallet-Address": walletAddress,
47
51
  "X-Wallet-Signature": signature,
@@ -50,7 +54,7 @@ function useWalletAuthHeaders() {
50
54
  // Cache for 4 minutes so repeated fetches don't re-prompt the user
51
55
  headerCache.set(walletAddress, { headers, expiresAt: Date.now() + 4 * 60 * 1000 });
52
56
  return headers;
53
- }, [address, signMessageAsync]);
57
+ }, [address, signMessage]);
54
58
  return { address, getHeaders };
55
59
  }
56
60
  function useKycStatus(enabled = true) {
@@ -3,7 +3,16 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.AuthButton = AuthButton;
4
4
  const jsx_runtime_1 = require("react/jsx-runtime");
5
5
  const Button_1 = require("../../custom/Button");
6
+ const lucide_react_1 = require("lucide-react");
6
7
  const signInUtils_1 = require("../utils/signInUtils");
8
+ const fallbackIcons = {
9
+ github: lucide_react_1.Github,
10
+ email: lucide_react_1.Mail,
11
+ };
7
12
  function AuthButton({ strategy, onClick, isLoading, }) {
8
- return ((0, jsx_runtime_1.jsx)(Button_1.Button, { onClick: onClick, disabled: isLoading, className: "flex w-full items-center justify-center bg-gray-100 px-2 py-3 hover:bg-gray-200 dark:bg-gray-800 dark:hover:bg-gray-700", children: (0, jsx_runtime_1.jsx)("img", { src: signInUtils_1.strategyIcons[strategy], className: "h-9 w-9" }) }, strategy));
13
+ const strategyIcon = signInUtils_1.strategyIcons[strategy];
14
+ const strategyLabel = signInUtils_1.strategyLabels[strategy] || strategy;
15
+ const FallbackIcon = fallbackIcons[strategy];
16
+ const buttonLabel = `Sign in with ${strategyLabel}`;
17
+ return ((0, jsx_runtime_1.jsx)(Button_1.Button, { onClick: onClick, disabled: isLoading, "aria-label": buttonLabel, title: buttonLabel, className: "flex w-full items-center justify-center bg-gray-100 px-2 py-3 hover:bg-gray-200 dark:bg-gray-800 dark:hover:bg-gray-700", children: strategyIcon ? ((0, jsx_runtime_1.jsx)("img", { src: strategyIcon, alt: `${strategyLabel} icon`, className: "h-9 w-9" })) : FallbackIcon ? ((0, jsx_runtime_1.jsx)(FallbackIcon, { className: "h-9 w-9 text-gray-900 dark:text-gray-100" })) : ((0, jsx_runtime_1.jsx)("span", { className: "text-sm font-semibold text-gray-900 dark:text-gray-100", children: strategyLabel.charAt(0) })) }, strategy));
9
18
  }
@@ -3,39 +3,53 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.LoginStepCustom = LoginStepCustom;
4
4
  const jsx_runtime_1 = require("react/jsx-runtime");
5
5
  const react_1 = require("../../../../../global-account/react");
6
+ const constants_1 = require("../../../../../shared/constants");
6
7
  const debug_1 = require("../../../../../shared/utils/debug");
7
8
  const thirdweb_1 = require("../../../../../shared/utils/thirdweb");
8
9
  const react_2 = require("react");
9
10
  const react_3 = require("thirdweb/react");
10
11
  const wallets_1 = require("thirdweb/wallets");
11
12
  const debug = (0, debug_1.debugB3React)("LoginStepCustom");
13
+ const EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
12
14
  function LoginStepCustom({ onSuccess, onError, chain, strategies, maxInitialWallets = 2, automaticallySetFirstEoa, }) {
13
15
  const { partnerId } = (0, react_1.useB3Config)();
14
16
  const [isLoading, setIsLoading] = (0, react_2.useState)(false);
15
17
  const [showAllWallets, setShowAllWallets] = (0, react_2.useState)(false);
18
+ const [showEmailFlow, setShowEmailFlow] = (0, react_2.useState)(false);
19
+ const [email, setEmail] = (0, react_2.useState)("");
20
+ const [verificationCode, setVerificationCode] = (0, react_2.useState)("");
21
+ const [emailCodeSent, setEmailCodeSent] = (0, react_2.useState)(false);
22
+ const [emailError, setEmailError] = (0, react_2.useState)(null);
16
23
  const { connect } = (0, react_1.useConnect)(partnerId, chain);
17
24
  const setIsAuthenticating = (0, react_1.useAuthStore)(state => state.setIsAuthenticating);
18
- const setIsAuthenticated = (0, react_1.useAuthStore)(state => state.setIsAuthenticated);
19
- const { logout } = (0, react_1.useAuthentication)(partnerId, { skipAutoConnect: true });
25
+ const { connect: onAuthConnect, logout } = (0, react_1.useAuthentication)(partnerId, { skipAutoConnect: true });
20
26
  const { connect: connectTW } = (0, react_3.useConnect)();
27
+ const connectedWallets = (0, react_3.useConnectedWallets)();
21
28
  // Split strategies into auth and wallet types
22
29
  const authStrategies = strategies.filter(s => !(0, react_1.isWalletType)(s));
23
30
  const walletStrategies = strategies.filter(react_1.isWalletType);
24
31
  const initialWallets = walletStrategies.slice(0, maxInitialWallets);
25
32
  const additionalWallets = walletStrategies.slice(maxInitialWallets);
26
- const handleConnect = async (strategy) => {
33
+ const authGridColumns = Math.max(1, Math.min(authStrategies.length, 4));
34
+ const resetEmailFlow = () => {
35
+ setShowEmailFlow(false);
36
+ setEmailCodeSent(false);
37
+ setVerificationCode("");
38
+ setEmailError(null);
39
+ };
40
+ const connectWithOptions = async (strategy, options) => {
27
41
  try {
28
42
  setIsLoading(true);
29
43
  debug("setIsAuthenticating:true:3");
30
44
  setIsAuthenticating(true);
31
- const options = (0, react_1.getConnectOptionsFromStrategy)(strategy);
32
45
  let connectResult;
33
- if (automaticallySetFirstEoa) {
34
- if (!options.wallet?.id) {
46
+ if (automaticallySetFirstEoa && (0, react_1.isWalletType)(strategy) && options.strategy === "wallet") {
47
+ const walletId = options.wallet?.id;
48
+ if (!walletId) {
35
49
  throw new Error("Wallet ID is required");
36
50
  }
37
51
  connectResult = await connectTW(async () => {
38
- const wallet = (0, wallets_1.createWallet)(options.wallet?.id);
52
+ const wallet = (0, wallets_1.createWallet)(walletId);
39
53
  await wallet.connect({
40
54
  client: thirdweb_1.client,
41
55
  });
@@ -43,20 +57,27 @@ function LoginStepCustom({ onSuccess, onError, chain, strategies, maxInitialWall
43
57
  });
44
58
  }
45
59
  else {
46
- // @ts-expect-error we have custom strategies too and we also get things like "apple" isn't assignable to "wallet"
47
60
  connectResult = await connect(options);
48
61
  }
49
62
  const account = connectResult?.getAccount();
50
63
  debug("@@connectResult", { connectResult, account, options });
51
- if (!account)
64
+ if (!account || !connectResult)
52
65
  throw new Error("Failed to connect");
66
+ const allConnectedWallets = connectedWallets.length > 0 && connectedWallets.some(wallet => wallet.id === connectResult.id)
67
+ ? connectedWallets
68
+ : [connectResult, ...connectedWallets.filter(wallet => wallet.id !== connectResult.id)];
69
+ await onAuthConnect(connectResult, allConnectedWallets);
53
70
  await onSuccess(account);
54
- setIsAuthenticated(true);
71
+ if (strategy === "email") {
72
+ resetEmailFlow();
73
+ }
55
74
  }
56
75
  catch (error) {
76
+ if (strategy === "email") {
77
+ setEmailError(error instanceof Error ? error.message : "Failed to sign in with email");
78
+ }
57
79
  await onError?.(error);
58
80
  await logout();
59
- setIsAuthenticated(false);
60
81
  }
61
82
  finally {
62
83
  setIsLoading(false);
@@ -64,8 +85,68 @@ function LoginStepCustom({ onSuccess, onError, chain, strategies, maxInitialWall
64
85
  setIsAuthenticating(false);
65
86
  }
66
87
  };
67
- return ((0, jsx_runtime_1.jsxs)(react_1.LoginStepContainer, { partnerId: partnerId, children: [authStrategies.length > 0 && ((0, jsx_runtime_1.jsx)("div", { className: `mb-6 w-full ${authStrategies.length <= 3 ? "space-y-3 px-3" : "grid grid-cols-4 gap-4"}`, children: authStrategies.map(strategy => {
68
- console.log("strategy", strategy);
69
- return ((0, jsx_runtime_1.jsx)(react_1.AuthButton, { strategy: strategy, onClick: () => handleConnect(strategy), isLoading: isLoading }, strategy));
70
- }) })), (0, jsx_runtime_1.jsx)("div", { className: "mb-4 w-full space-y-2", children: initialWallets.map(walletId => ((0, jsx_runtime_1.jsx)(react_1.WalletRow, { walletId: walletId, onClick: () => handleConnect(walletId), isLoading: isLoading }, walletId))) }), additionalWallets.length > 0 && ((0, jsx_runtime_1.jsxs)("div", { className: "w-full", children: [(0, jsx_runtime_1.jsx)(react_1.Button, { onClick: () => setShowAllWallets(!showAllWallets), className: "mb-2 w-full bg-transparent text-gray-600 hover:bg-gray-100", children: showAllWallets ? "Show less" : "More options" }), showAllWallets && ((0, jsx_runtime_1.jsx)("div", { className: "max-h-60 space-y-2 overflow-y-auto", children: additionalWallets.map(walletId => ((0, jsx_runtime_1.jsx)(react_1.WalletRow, { walletId: walletId, onClick: () => handleConnect(walletId), isLoading: isLoading }, walletId))) }))] }))] }));
88
+ const handleConnect = async (strategy) => {
89
+ if (strategy === "email") {
90
+ setShowEmailFlow(true);
91
+ setEmailCodeSent(false);
92
+ setVerificationCode("");
93
+ setEmailError(null);
94
+ return;
95
+ }
96
+ const options = (0, react_1.getConnectOptionsFromStrategy)(strategy);
97
+ await connectWithOptions(strategy, options);
98
+ };
99
+ const handleSendEmailCode = async () => {
100
+ const normalizedEmail = email.trim().toLowerCase();
101
+ if (!normalizedEmail) {
102
+ setEmailError("Please enter your email address");
103
+ return;
104
+ }
105
+ if (!EMAIL_REGEX.test(normalizedEmail)) {
106
+ setEmailError("Please enter a valid email address");
107
+ return;
108
+ }
109
+ try {
110
+ setIsLoading(true);
111
+ setEmailError(null);
112
+ await (0, wallets_1.preAuthenticate)({
113
+ client: thirdweb_1.client,
114
+ strategy: "email",
115
+ email: normalizedEmail,
116
+ ecosystem: {
117
+ id: constants_1.ecosystemWalletId,
118
+ partnerId,
119
+ },
120
+ });
121
+ setEmail(normalizedEmail);
122
+ setEmailCodeSent(true);
123
+ }
124
+ catch (error) {
125
+ setEmailError(error instanceof Error ? error.message : "Failed to send verification code");
126
+ await onError?.(error);
127
+ }
128
+ finally {
129
+ setIsLoading(false);
130
+ }
131
+ };
132
+ const handleEmailLogin = async () => {
133
+ const normalizedEmail = email.trim().toLowerCase();
134
+ const normalizedCode = verificationCode.trim();
135
+ if (!EMAIL_REGEX.test(normalizedEmail)) {
136
+ setEmailError("Please enter a valid email address");
137
+ return;
138
+ }
139
+ if (!normalizedCode) {
140
+ setEmailError("Please enter your verification code");
141
+ return;
142
+ }
143
+ await connectWithOptions("email", {
144
+ strategy: "email",
145
+ email: normalizedEmail,
146
+ verificationCode: normalizedCode,
147
+ });
148
+ };
149
+ return ((0, jsx_runtime_1.jsx)(react_1.LoginStepContainer, { partnerId: partnerId, children: showEmailFlow ? ((0, jsx_runtime_1.jsxs)("div", { className: "mb-6 w-full space-y-3 px-3", children: [(0, jsx_runtime_1.jsx)("p", { className: "text-center text-sm font-medium text-gray-900 dark:text-gray-100", children: "Sign in with email" }), (0, jsx_runtime_1.jsx)(react_1.Input, { type: "email", placeholder: "you@example.com", value: email, onChange: event => setEmail(event.target.value), disabled: isLoading || emailCodeSent }), emailCodeSent && ((0, jsx_runtime_1.jsx)(react_1.Input, { type: "text", placeholder: "Enter verification code", value: verificationCode, onChange: event => setVerificationCode(event.target.value), disabled: isLoading })), emailError && (0, jsx_runtime_1.jsx)("p", { className: "text-sm text-red-500", children: emailError }), (0, jsx_runtime_1.jsx)(react_1.Button, { onClick: emailCodeSent ? handleEmailLogin : handleSendEmailCode, disabled: isLoading, className: "w-full", children: isLoading ? "Loading..." : emailCodeSent ? "Verify code" : "Send code" }), emailCodeSent && ((0, jsx_runtime_1.jsx)(react_1.Button, { variant: "outline", onClick: handleSendEmailCode, disabled: isLoading, className: "w-full", children: "Resend code" })), (0, jsx_runtime_1.jsx)(react_1.Button, { variant: "outline", onClick: resetEmailFlow, disabled: isLoading, className: "w-full", children: "Back" })] })) : ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [authStrategies.length > 0 && ((0, jsx_runtime_1.jsx)("div", { className: `mb-6 grid w-full gap-4 px-3 ${authStrategies.length > 4 ? "grid-cols-4" : ""}`, style: authStrategies.length <= 4
150
+ ? { gridTemplateColumns: `repeat(${authGridColumns}, minmax(0, 1fr))` }
151
+ : undefined, children: authStrategies.map(strategy => ((0, jsx_runtime_1.jsx)(react_1.AuthButton, { strategy: strategy, onClick: () => handleConnect(strategy), isLoading: isLoading }, strategy))) })), (0, jsx_runtime_1.jsx)("div", { className: "mb-4 w-full space-y-2", children: initialWallets.map(walletId => ((0, jsx_runtime_1.jsx)(react_1.WalletRow, { walletId: walletId, onClick: () => handleConnect(walletId), isLoading: isLoading }, walletId))) }), additionalWallets.length > 0 && ((0, jsx_runtime_1.jsxs)("div", { className: "w-full", children: [(0, jsx_runtime_1.jsx)(react_1.Button, { onClick: () => setShowAllWallets(!showAllWallets), className: "mb-2 w-full bg-transparent text-gray-600 hover:bg-gray-100", children: showAllWallets ? "Show less" : "More options" }), showAllWallets && ((0, jsx_runtime_1.jsx)("div", { className: "max-h-60 space-y-2 overflow-y-auto", children: additionalWallets.map(walletId => ((0, jsx_runtime_1.jsx)(react_1.WalletRow, { walletId: walletId, onClick: () => handleConnect(walletId), isLoading: isLoading }, walletId))) }))] }))] })) }));
71
152
  }
@@ -3,15 +3,17 @@ import { SingleStepAuthArgsType, Wallet } from "thirdweb/wallets";
3
3
  type WalletType = Wallet["id"];
4
4
  type StrategyType = SingleStepAuthArgsType["strategy"];
5
5
  type CustomStrategyType = "basement" | "privy";
6
- type AllowedStrategies = StrategyType | WalletType | CustomStrategyType;
7
- export declare const allowedStrategies: readonly ["apple", "google", "x", "discord", "guest", "walletConnect", "io.metamask", "com.coinbase.wallet", "basement", "privy"];
6
+ type AllowedStrategies = StrategyType | WalletType | CustomStrategyType | "email";
7
+ type NonWalletStrategyType = Exclude<AllowedStrategies, WalletType>;
8
+ export declare const allowedStrategies: readonly ["apple", "google", "github", "x", "discord", "email", "guest", "walletConnect", "io.metamask", "com.coinbase.wallet", "basement", "privy"];
8
9
  export type AllowedStrategy = (typeof allowedStrategies)[number];
9
10
  export declare function isWalletType(strategy: AllowedStrategies): strategy is WalletType;
10
- export declare function isStrategyType(strategy: AllowedStrategies): strategy is StrategyType;
11
+ export declare function isStrategyType(strategy: AllowedStrategies): strategy is NonWalletStrategyType;
11
12
  export declare function getConnectOptionsFromStrategy(strategy: AllowedStrategy): {
12
13
  strategy: StrategyType;
13
14
  wallet?: Wallet;
14
15
  chain?: Chain;
15
16
  };
16
17
  export declare const strategyIcons: Record<string, string>;
18
+ export declare const strategyLabels: Record<string, string>;
17
19
  export {};
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.strategyIcons = exports.allowedStrategies = void 0;
3
+ exports.strategyLabels = exports.strategyIcons = exports.allowedStrategies = void 0;
4
4
  exports.isWalletType = isWalletType;
5
5
  exports.isStrategyType = isStrategyType;
6
6
  exports.getConnectOptionsFromStrategy = getConnectOptionsFromStrategy;
@@ -11,9 +11,10 @@ exports.allowedStrategies = [
11
11
  // Auth strategies
12
12
  "apple",
13
13
  "google",
14
+ "github",
14
15
  "x",
15
16
  "discord",
16
- // "github",
17
+ "email",
17
18
  "guest",
18
19
  // Wallet IDs
19
20
  "walletConnect",
@@ -35,6 +36,9 @@ function getConnectOptionsFromStrategy(strategy) {
35
36
  if (!exports.allowedStrategies.includes(strategy)) {
36
37
  throw new Error(`Invalid strategy: ${strategy}`);
37
38
  }
39
+ if (strategy === "email") {
40
+ throw new Error("Email strategy requires OTP flow and cannot be connected in a single step");
41
+ }
38
42
  if (isWalletType(strategy)) {
39
43
  return { strategy: "wallet", wallet: (0, wallets_1.createWallet)(strategy) };
40
44
  }
@@ -51,6 +55,15 @@ exports.strategyIcons = {
51
55
  guest: "https://cdn.b3.fun/incognito.svg",
52
56
  // Add more strategies as needed
53
57
  };
58
+ exports.strategyLabels = {
59
+ google: "Google",
60
+ x: "X",
61
+ discord: "Discord",
62
+ apple: "Apple",
63
+ guest: "Guest",
64
+ github: "GitHub",
65
+ email: "Email",
66
+ };
54
67
  // Test it
55
68
  // console.log(getConnectOptionsFromStrategy("io.metamask"));
56
69
  // console.log(getConnectOptionsFromStrategy("google"));
@@ -1,9 +1,9 @@
1
1
  import { Chain } from "thirdweb";
2
- import { SingleStepAuthArgsType } from "thirdweb/wallets";
2
+ import { MultiStepAuthArgsType, SingleStepAuthArgsType } from "thirdweb/wallets";
3
3
  /**
4
4
  * This hook is used to connect to a wallet using the thirdweb client.
5
5
  */
6
6
  export declare function useConnect(partnerId: string, chain?: Chain): {
7
- connect: (strategyOptions?: SingleStepAuthArgsType) => Promise<import("thirdweb/wallets").Wallet | null>;
7
+ connect: (strategyOptions?: MultiStepAuthArgsType | SingleStepAuthArgsType) => Promise<import("thirdweb/wallets").Wallet | null>;
8
8
  isLoading: boolean;
9
9
  };
@@ -53,9 +53,7 @@ export function CheckoutPaymentPanel({ recipientAddress, destinationTokenAddress
53
53
  if (activeOrderId) {
54
54
  return (_jsx(CheckoutOrderStatus, { orderId: activeOrderId, themeColor: themeColor, returnUrl: returnUrl, returnLabel: returnLabel, onSuccess: onSuccess, onError: onError, onRetry: handleRetry, showPoints: showPoints, showOrderId: showOrderId, classes: classes }));
55
55
  }
56
- const accordionButtonClass = (active) => cn("anyspend-payment-method-btn flex w-full items-center gap-3 px-4 py-4 text-left transition-colors", active
57
- ? "bg-white dark:bg-neutral-900"
58
- : "bg-white hover:bg-gray-50 dark:bg-neutral-900 dark:hover:bg-neutral-800", classes?.paymentMethodButton);
59
- const expandedPanelClass = cn("anyspend-payment-method-panel border-t border-gray-100 bg-white px-4 py-4 dark:border-neutral-800 dark:bg-neutral-900");
56
+ const accordionButtonClass = (active) => cn("anyspend-payment-method-btn flex w-full items-center gap-3 px-4 py-4 text-left transition-colors", !active && "hover:bg-gray-50 dark:hover:bg-neutral-800", classes?.paymentMethodButton);
57
+ const expandedPanelClass = cn("anyspend-payment-method-panel border-t border-gray-100 px-4 py-4 dark:border-neutral-800");
60
58
  return (_jsxs("div", { className: cn("anyspend-payment-panel flex flex-col gap-5", classes?.paymentPanel), children: [_jsx("h2", { className: cn("anyspend-payment-title text-lg font-semibold text-gray-900 dark:text-gray-100", classes?.paymentTitle), children: "Payment" }), !isFormValid && (_jsx("p", { className: "text-sm text-amber-600 dark:text-amber-400", children: "Please complete the required fields above before proceeding to payment." })), _jsxs("div", { className: cn("anyspend-payment-methods divide-y divide-gray-200 overflow-hidden rounded-xl border border-gray-200 dark:divide-neutral-700 dark:border-neutral-700", !isFormValid && "pointer-events-none opacity-50", classes?.paymentMethodSelector), children: [_jsxs("div", { className: "anyspend-method-crypto", children: [_jsxs("button", { onClick: () => setPaymentMethod(paymentMethod === "crypto" ? null : "crypto"), className: accordionButtonClass(paymentMethod === "crypto"), children: [_jsx(RadioCircle, { selected: paymentMethod === "crypto", themeColor: themeColor }), _jsx(Wallet, { className: "h-5 w-5 text-gray-700 dark:text-gray-300" }), _jsx("span", { className: "text-sm font-medium text-gray-900 dark:text-gray-100", children: "Pay with crypto" })] }), _jsx(AnimatePresence, { initial: false, children: paymentMethod === "crypto" && (_jsx(motion.div, { initial: { height: 0, opacity: 0 }, animate: { height: "auto", opacity: 1 }, exit: { height: 0, opacity: 0 }, transition: { duration: 0.2, ease: "easeOut" }, style: { overflow: "hidden" }, children: _jsx("div", { className: expandedPanelClass, children: _jsx(CryptoPayPanel, { recipientAddress: recipientAddress, destinationTokenAddress: destinationTokenAddress, destinationTokenChainId: destinationTokenChainId, totalAmount: totalAmount, buttonText: buttonText, themeColor: themeColor, onOrderCreated: handleOrderCreated, onError: onError, callbackMetadata: callbackMetadata, classes: classes, senderAddress: senderAddress }) }) }, "crypto-panel")) })] }), _jsxs("div", { className: "anyspend-method-card", children: [_jsxs("button", { onClick: () => setPaymentMethod(paymentMethod === "card" ? null : "card"), className: accordionButtonClass(paymentMethod === "card"), children: [_jsx(RadioCircle, { selected: paymentMethod === "card", themeColor: themeColor }), _jsx(CreditCard, { className: "h-5 w-5 text-gray-700 dark:text-gray-300" }), _jsx("span", { className: "text-sm font-medium text-gray-900 dark:text-gray-100", children: "Credit or debit card" }), _jsxs("div", { className: "ml-auto flex items-center gap-1", children: [_jsx(VisaLogo, {}), _jsx(MastercardLogo, {}), _jsx(AmexLogo, {})] })] }), _jsx(AnimatePresence, { initial: false, children: paymentMethod === "card" && (_jsx(motion.div, { initial: { height: 0, opacity: 0 }, animate: { height: "auto", opacity: 1 }, exit: { height: 0, opacity: 0 }, transition: { duration: 0.2, ease: "easeOut" }, style: { overflow: "hidden" }, children: _jsx("div", { className: expandedPanelClass, children: _jsx(FiatCheckoutPanel, { recipientAddress: recipientAddress, destinationTokenAddress: destinationTokenAddress, destinationTokenChainId: destinationTokenChainId, totalAmount: totalAmount, themeColor: themeColor, onOrderCreated: handleOrderCreated, onError: onError, callbackMetadata: callbackMetadata, classes: classes, feeOnTop: feeOnTop, kycEnabled: kycEnabled }) }) }, "card-panel")) })] }), _jsxs("div", { className: "anyspend-method-coinbase", children: [_jsxs("button", { onClick: () => setPaymentMethod(paymentMethod === "coinbase" ? null : "coinbase"), className: accordionButtonClass(paymentMethod === "coinbase"), children: [_jsx(RadioCircle, { selected: paymentMethod === "coinbase", themeColor: themeColor }), _jsx(CoinbaseLogo, {}), _jsx("span", { className: "text-sm font-medium text-gray-900 dark:text-gray-100", children: "Coinbase Pay" })] }), _jsx(AnimatePresence, { initial: false, children: paymentMethod === "coinbase" && (_jsx(motion.div, { initial: { height: 0, opacity: 0 }, animate: { height: "auto", opacity: 1 }, exit: { height: 0, opacity: 0 }, transition: { duration: 0.2, ease: "easeOut" }, style: { overflow: "hidden" }, children: _jsx("div", { className: expandedPanelClass, children: _jsx(CoinbaseCheckoutPanel, { recipientAddress: recipientAddress, destinationTokenAddress: destinationTokenAddress, destinationTokenChainId: destinationTokenChainId, totalAmount: totalAmount, themeColor: themeColor, onOrderCreated: handleOrderCreated, onError: onError, callbackMetadata: callbackMetadata, classes: classes }) }) }, "coinbase-panel")) })] })] })] }));
61
59
  }
@@ -7,8 +7,7 @@ import { useMutation } from "@tanstack/react-query";
7
7
  import { useMemo } from "react";
8
8
  import { parseUnits } from "viem";
9
9
  import { base } from "viem/chains";
10
- import { useAccount } from "wagmi";
11
- import { getCachedWalletHeaders } from "./useKycStatus.js";
10
+ import { getCachedWalletHeaders, useWalletAuthHeaders } from "./useKycStatus.js";
12
11
  import { useValidatedClientReferenceId } from "./useValidatedClientReferenceId.js";
13
12
  /**
14
13
  * Hook for creating onramp orders in the Anyspend protocol
@@ -17,7 +16,7 @@ import { useValidatedClientReferenceId } from "./useValidatedClientReferenceId.j
17
16
  export function useAnyspendCreateOnrampOrder({ onSuccess, onError } = {}) {
18
17
  // Get B3 context values
19
18
  const { partnerId } = useB3Config();
20
- const { address } = useAccount();
19
+ const { address, getHeaders: getWalletAuthHeaders } = useWalletAuthHeaders();
21
20
  // Get validated client reference ID from B3 context
22
21
  const createValidatedClientReferenceId = useValidatedClientReferenceId();
23
22
  // Get fingerprint data
@@ -41,12 +40,11 @@ export function useAnyspendCreateOnrampOrder({ onSuccess, onError } = {}) {
41
40
  const srcAmountOnRampInWei = parseUnits(srcFiatAmount, USDC_BASE.decimals);
42
41
  // For card payments, include wallet auth headers so the backend can verify
43
42
  // KYC by the signing wallet address (may differ from the B3 JWT address).
44
- // Only use already-cached headers never trigger a fresh wallet signature
45
- // here, as that would prompt the user without their explicit consent.
46
- // KycGate pre-caches the headers in the "Continue to Verify" user-gesture.
43
+ // First try cached headers (from KycGate), then sign on-the-fly.
44
+ // The user is already clicking "Continue"/"Pay" so signing here is acceptable.
47
45
  let kycWalletHeaders;
48
46
  if (onramp.vendor === "stripe-web2" && address) {
49
- kycWalletHeaders = getCachedWalletHeaders(address);
47
+ kycWalletHeaders = getCachedWalletHeaders(address) || (await getWalletAuthHeaders());
50
48
  }
51
49
  return await anyspendService.createOrder({
52
50
  recipientAddress: normalizeAddress(recipientAddress),
@@ -24,10 +24,12 @@ interface KycVerifyResponse {
24
24
  export declare function getCachedWalletHeaders(address: string): Record<string, string> | undefined;
25
25
  /**
26
26
  * Returns a function that builds the wallet-signature auth headers.
27
+ * Uses useAccountWallet (thirdweb) instead of wagmi so signing works
28
+ * for all wallet types (social login, EOA, smart wallet, etc.).
27
29
  * Caches signatures for 4 minutes (server allows 5-minute window).
28
30
  */
29
31
  export declare function useWalletAuthHeaders(): {
30
- address: `0x${string}` | undefined;
32
+ address: string | undefined;
31
33
  getHeaders: () => Promise<Record<string, string>>;
32
34
  };
33
35
  export declare function useKycStatus(enabled?: boolean): {
@@ -1,8 +1,8 @@
1
1
  "use client";
2
2
  import { ANYSPEND_MAINNET_BASE_URL } from "../../../anyspend/constants/index.js";
3
+ import { useAccountWallet } from "../../../global-account/react/index.js";
3
4
  import { useMutation, useQuery } from "@tanstack/react-query";
4
5
  import { useCallback } from "react";
5
- import { useAccount, useSignMessage } from "wagmi";
6
6
  function buildWalletAuthMessage(walletAddress, timestamp) {
7
7
  return `AnySpend wants to verify your identity for card payments.\n\nThis signature does not trigger a blockchain transaction or cost any gas.\n\nWallet: ${walletAddress.toLowerCase()}\nNonce: ${timestamp}`;
8
8
  }
@@ -20,21 +20,25 @@ export function getCachedWalletHeaders(address) {
20
20
  }
21
21
  /**
22
22
  * Returns a function that builds the wallet-signature auth headers.
23
+ * Uses useAccountWallet (thirdweb) instead of wagmi so signing works
24
+ * for all wallet types (social login, EOA, smart wallet, etc.).
23
25
  * Caches signatures for 4 minutes (server allows 5-minute window).
24
26
  */
25
27
  export function useWalletAuthHeaders() {
26
- const { address } = useAccount();
27
- const { signMessageAsync } = useSignMessage();
28
+ const { address, wallet } = useAccountWallet();
29
+ const signMessage = wallet.signMessage;
28
30
  const getHeaders = useCallback(async () => {
29
31
  if (!address)
30
32
  throw new Error("No wallet connected");
33
+ if (!signMessage)
34
+ throw new Error("Wallet does not support message signing");
31
35
  const walletAddress = address.toLowerCase();
32
36
  const cached = headerCache.get(walletAddress);
33
37
  if (cached && Date.now() < cached.expiresAt)
34
38
  return cached.headers;
35
39
  const timestamp = Math.floor(Date.now() / 1000);
36
40
  const message = buildWalletAuthMessage(walletAddress, timestamp);
37
- const signature = await signMessageAsync({ message });
41
+ const signature = await signMessage({ message });
38
42
  const headers = {
39
43
  "X-Wallet-Address": walletAddress,
40
44
  "X-Wallet-Signature": signature,
@@ -43,7 +47,7 @@ export function useWalletAuthHeaders() {
43
47
  // Cache for 4 minutes so repeated fetches don't re-prompt the user
44
48
  headerCache.set(walletAddress, { headers, expiresAt: Date.now() + 4 * 60 * 1000 });
45
49
  return headers;
46
- }, [address, signMessageAsync]);
50
+ }, [address, signMessage]);
47
51
  return { address, getHeaders };
48
52
  }
49
53
  export function useKycStatus(enabled = true) {
@@ -1,6 +1,15 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { Button } from "../../custom/Button.js";
3
- import { strategyIcons } from "../utils/signInUtils.js";
3
+ import { Github, Mail } from "lucide-react";
4
+ import { strategyIcons, strategyLabels } from "../utils/signInUtils.js";
5
+ const fallbackIcons = {
6
+ github: Github,
7
+ email: Mail,
8
+ };
4
9
  export function AuthButton({ strategy, onClick, isLoading, }) {
5
- return (_jsx(Button, { onClick: onClick, disabled: isLoading, className: "flex w-full items-center justify-center bg-gray-100 px-2 py-3 hover:bg-gray-200 dark:bg-gray-800 dark:hover:bg-gray-700", children: _jsx("img", { src: strategyIcons[strategy], className: "h-9 w-9" }) }, strategy));
10
+ const strategyIcon = strategyIcons[strategy];
11
+ const strategyLabel = strategyLabels[strategy] || strategy;
12
+ const FallbackIcon = fallbackIcons[strategy];
13
+ const buttonLabel = `Sign in with ${strategyLabel}`;
14
+ return (_jsx(Button, { onClick: onClick, disabled: isLoading, "aria-label": buttonLabel, title: buttonLabel, className: "flex w-full items-center justify-center bg-gray-100 px-2 py-3 hover:bg-gray-200 dark:bg-gray-800 dark:hover:bg-gray-700", children: strategyIcon ? (_jsx("img", { src: strategyIcon, alt: `${strategyLabel} icon`, className: "h-9 w-9" })) : FallbackIcon ? (_jsx(FallbackIcon, { className: "h-9 w-9 text-gray-900 dark:text-gray-100" })) : (_jsx("span", { className: "text-sm font-semibold text-gray-900 dark:text-gray-100", children: strategyLabel.charAt(0) })) }, strategy));
6
15
  }