@b3dotfun/sdk 0.0.77 → 0.0.78-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/dist/cjs/anyspend/react/components/common/PanelOnramp.js +1 -1
  2. package/dist/cjs/global-account/react/components/B3DynamicModal.js +18 -4
  3. package/dist/cjs/global-account/react/components/B3Provider/B3Provider.d.ts +4 -2
  4. package/dist/cjs/global-account/react/components/B3Provider/B3Provider.js +6 -3
  5. package/dist/cjs/global-account/react/components/B3Provider/types.d.ts +1 -0
  6. package/dist/cjs/global-account/react/components/B3Provider/types.js +1 -0
  7. package/dist/cjs/global-account/react/components/ManageAccount/BottomNavigation.js +2 -2
  8. package/dist/cjs/global-account/react/components/SignInWithB3/SignInWithB3Flow.js +162 -46
  9. package/dist/cjs/global-account/react/components/TurnkeyAuthModal.d.ts +8 -0
  10. package/dist/cjs/global-account/react/components/TurnkeyAuthModal.js +84 -0
  11. package/dist/cjs/global-account/react/components/index.d.ts +1 -0
  12. package/dist/cjs/global-account/react/components/index.js +6 -3
  13. package/dist/cjs/global-account/react/hooks/index.d.ts +1 -0
  14. package/dist/cjs/global-account/react/hooks/index.js +3 -1
  15. package/dist/cjs/global-account/react/hooks/useAuthentication.d.ts +7 -0
  16. package/dist/cjs/global-account/react/hooks/useTurnkeyAuth.d.ts +20 -0
  17. package/dist/cjs/global-account/react/hooks/useTurnkeyAuth.js +112 -0
  18. package/dist/cjs/global-account/react/hooks/useUserQuery.d.ts +7 -0
  19. package/dist/cjs/global-account/react/stores/useAuthStore.d.ts +2 -0
  20. package/dist/cjs/global-account/react/stores/useAuthStore.js +2 -0
  21. package/dist/cjs/global-account/react/stores/useModalStore.d.ts +21 -1
  22. package/dist/esm/anyspend/react/components/common/PanelOnramp.js +1 -1
  23. package/dist/esm/global-account/react/components/B3DynamicModal.js +18 -4
  24. package/dist/esm/global-account/react/components/B3Provider/B3Provider.d.ts +4 -2
  25. package/dist/esm/global-account/react/components/B3Provider/B3Provider.js +6 -3
  26. package/dist/esm/global-account/react/components/B3Provider/types.d.ts +1 -0
  27. package/dist/esm/global-account/react/components/B3Provider/types.js +1 -0
  28. package/dist/esm/global-account/react/components/ManageAccount/BottomNavigation.js +2 -2
  29. package/dist/esm/global-account/react/components/SignInWithB3/SignInWithB3Flow.js +162 -46
  30. package/dist/esm/global-account/react/components/TurnkeyAuthModal.d.ts +8 -0
  31. package/dist/esm/global-account/react/components/TurnkeyAuthModal.js +81 -0
  32. package/dist/esm/global-account/react/components/index.d.ts +1 -0
  33. package/dist/esm/global-account/react/components/index.js +2 -0
  34. package/dist/esm/global-account/react/hooks/index.d.ts +1 -0
  35. package/dist/esm/global-account/react/hooks/index.js +1 -0
  36. package/dist/esm/global-account/react/hooks/useAuthentication.d.ts +7 -0
  37. package/dist/esm/global-account/react/hooks/useTurnkeyAuth.d.ts +20 -0
  38. package/dist/esm/global-account/react/hooks/useTurnkeyAuth.js +106 -0
  39. package/dist/esm/global-account/react/hooks/useUserQuery.d.ts +7 -0
  40. package/dist/esm/global-account/react/stores/useAuthStore.d.ts +2 -0
  41. package/dist/esm/global-account/react/stores/useAuthStore.js +2 -0
  42. package/dist/esm/global-account/react/stores/useModalStore.d.ts +21 -1
  43. package/dist/styles/index.css +1 -1
  44. package/dist/types/global-account/react/components/B3Provider/B3Provider.d.ts +4 -2
  45. package/dist/types/global-account/react/components/B3Provider/types.d.ts +1 -0
  46. package/dist/types/global-account/react/components/TurnkeyAuthModal.d.ts +8 -0
  47. package/dist/types/global-account/react/components/index.d.ts +1 -0
  48. package/dist/types/global-account/react/hooks/index.d.ts +1 -0
  49. package/dist/types/global-account/react/hooks/useAuthentication.d.ts +7 -0
  50. package/dist/types/global-account/react/hooks/useTurnkeyAuth.d.ts +20 -0
  51. package/dist/types/global-account/react/hooks/useUserQuery.d.ts +7 -0
  52. package/dist/types/global-account/react/stores/useAuthStore.d.ts +2 -0
  53. package/dist/types/global-account/react/stores/useModalStore.d.ts +21 -1
  54. package/package.json +2 -2
  55. package/src/anyspend/react/components/common/PanelOnramp.tsx +10 -8
  56. package/src/global-account/react/components/B3DynamicModal.tsx +26 -3
  57. package/src/global-account/react/components/B3Provider/B3Provider.tsx +9 -0
  58. package/src/global-account/react/components/B3Provider/types.ts +2 -0
  59. package/src/global-account/react/components/ManageAccount/BottomNavigation.tsx +3 -3
  60. package/src/global-account/react/components/SignInWithB3/SignInWithB3Flow.tsx +170 -48
  61. package/src/global-account/react/components/TurnkeyAuthModal.tsx +240 -0
  62. package/src/global-account/react/components/index.ts +3 -0
  63. package/src/global-account/react/hooks/index.ts +1 -0
  64. package/src/global-account/react/hooks/useTurnkeyAuth.ts +138 -0
  65. package/src/global-account/react/stores/useAuthStore.ts +4 -0
  66. package/src/global-account/react/stores/useModalStore.ts +22 -0
@@ -82,7 +82,7 @@ function PanelOnramp({ srcAmountOnRamp, setSrcAmountOnRamp, selectedPaymentMetho
82
82
  .filter(v => !isNaN(Number(v)))
83
83
  .map(value => ((0, jsx_runtime_1.jsxs)("button", { onClick: () => handleQuickAmount(value), className: `bg-as-surface-secondary border-as-border-secondary hover:border-as-border-secondary h-7 w-14 rounded-lg border text-sm font-medium transition-all duration-200 ${srcAmountOnRamp === value
84
84
  ? "border-as-border-secondary bg-as-surface-secondary"
85
- : "bg-as-surface-secondary hover:bg-as-surface-secondary"}`, children: ["$", value] }, value))) }), destinationToken && destinationChainId && !hideDstToken && ((0, jsx_runtime_1.jsx)(OrderTokenAmountFiat_1.OrderTokenAmountFiat, { address: _recipientAddress, context: "to", inputValue: destinationAmount || "0", onChangeInput: () => { }, chainId: destinationChainId, setChainId: onDestinationChainChange || (() => { }), token: destinationToken, setToken: onDestinationTokenChange || (() => { }) }))] }), (0, jsx_runtime_1.jsxs)("div", { className: "border-as-border-secondary bg-as-surface-secondary mt-4 flex w-full flex-col gap-3 rounded-xl border p-4", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex w-full items-center justify-between gap-2", children: [(0, jsx_runtime_1.jsx)("span", { className: "text-as-tertiarry flex items-center text-sm", children: "Recipient" }), _recipientAddress ? ((0, jsx_runtime_1.jsxs)("button", { className: "text-as-tertiarry flex h-7 items-center gap-1 text-sm transition-colors hover:text-blue-700", onClick: () => setActivePanel(recipientSelectionPanelIndex), children: [(0, jsx_runtime_1.jsx)("span", { className: "text-sm", children: recipientName ? (0, utils_1.formatUsername)(recipientName) : (0, formatAddress_1.formatAddress)(_recipientAddress) }), (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronRight, { size: 16 })] })) : ((0, jsx_runtime_1.jsxs)("button", { className: "text-as-tertiarry flex h-7 items-center gap-1 text-sm transition-colors hover:text-blue-700", onClick: () => setActivePanel(5), children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Wallet, { className: "text-as-brand", size: 16 }), "Select recipient", (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronRight, { size: 16 })] }))] }), (0, jsx_runtime_1.jsx)("div", { className: "divider w-full" }), (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-between", children: [(0, jsx_runtime_1.jsx)("span", { className: "text-as-tertiarry text-sm", children: "Expected to receive" }), (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2", children: [(0, jsx_runtime_1.jsxs)("span", { className: "text-as-primary font-semibold", children: [destinationAmount || "0", " ", dstTokenSymbol || destinationToken?.symbol || ""] }), (0, jsx_runtime_1.jsxs)("span", { className: "text-as-tertiarry text-sm", children: ["on ", destinationChainId ? chain_1.ALL_CHAINS[destinationChainId]?.name : ""] }), destinationToken && destinationChainId && destinationToken.metadata?.logoURI && ((0, jsx_runtime_1.jsx)("img", { src: chain_1.ALL_CHAINS[destinationChainId]?.logoUrl, alt: "Chain", className: "h-4 w-4 rounded-full" }))] })] }), (0, jsx_runtime_1.jsx)("div", { className: "divider w-full" }), (0, jsx_runtime_1.jsx)("div", { className: "", children: (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-between", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-1.5", children: [(0, jsx_runtime_1.jsx)("span", { className: "text-as-tertiarry text-sm", children: "Total" }), anyspendQuote?.data?.fee && onShowFeeDetail && ((0, jsx_runtime_1.jsx)("button", { onClick: onShowFeeDetail, className: "text-as-primary/40 hover:text-as-primary/60 transition-colors", children: (0, jsx_runtime_1.jsx)(lucide_react_1.Info, { className: "h-4 w-4" }) })), featureFlags.showPoints &&
85
+ : "bg-as-surface-secondary hover:bg-as-surface-secondary"}`, children: ["$", value] }, value))) }), destinationToken && destinationChainId && !hideDstToken && ((0, jsx_runtime_1.jsx)(OrderTokenAmountFiat_1.OrderTokenAmountFiat, { address: _recipientAddress, context: "to", inputValue: destinationAmount || "0", onChangeInput: () => { }, chainId: destinationChainId, setChainId: onDestinationChainChange || (() => { }), token: destinationToken, setToken: onDestinationTokenChange || (() => { }) }))] }), (0, jsx_runtime_1.jsxs)("div", { className: "border-as-border-secondary bg-as-surface-secondary mt-4 flex w-full flex-col gap-3 rounded-xl border p-4", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex w-full items-center justify-between gap-2", children: [(0, jsx_runtime_1.jsx)("span", { className: "text-as-tertiarry flex items-center text-sm", children: "Recipient" }), _recipientAddress ? ((0, jsx_runtime_1.jsxs)("button", { className: "text-as-tertiarry flex h-7 items-center gap-1 text-sm transition-colors hover:text-blue-700", onClick: () => setActivePanel(recipientSelectionPanelIndex), children: [(0, jsx_runtime_1.jsx)("span", { className: "text-sm", children: recipientName ? (0, utils_1.formatUsername)(recipientName) : (0, formatAddress_1.formatAddress)(_recipientAddress) }), (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronRight, { size: 16 })] })) : ((0, jsx_runtime_1.jsxs)("button", { className: "text-as-tertiarry flex h-7 items-center gap-1 text-sm transition-colors hover:text-blue-700", onClick: () => setActivePanel(5), children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Wallet, { className: "text-as-brand", size: 16 }), "Select recipient", (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronRight, { size: 16 })] }))] }), (0, jsx_runtime_1.jsx)("div", { className: "divider w-full" }), (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-between gap-4", children: [(0, jsx_runtime_1.jsx)("span", { className: "text-as-tertiarry text-sm", children: "Expected to receive" }), (0, jsx_runtime_1.jsxs)("div", { className: "flex flex-wrap items-center justify-end gap-1", children: [(0, jsx_runtime_1.jsxs)("span", { className: "text-as-primary font-semibold", children: [destinationAmount || "0", " ", dstTokenSymbol || destinationToken?.symbol || ""] }), (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-1", children: [(0, jsx_runtime_1.jsxs)("span", { className: "text-as-tertiarry text-sm", children: ["on ", destinationChainId ? chain_1.ALL_CHAINS[destinationChainId]?.name : ""] }), destinationToken && destinationChainId && destinationToken.metadata?.logoURI && ((0, jsx_runtime_1.jsx)("img", { src: chain_1.ALL_CHAINS[destinationChainId]?.logoUrl, alt: "Chain", className: "h-4 w-4 rounded-full" }))] })] })] }), (0, jsx_runtime_1.jsx)("div", { className: "divider w-full" }), (0, jsx_runtime_1.jsx)("div", { className: "", children: (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-between", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-1.5", children: [(0, jsx_runtime_1.jsx)("span", { className: "text-as-tertiarry text-sm", children: "Total" }), anyspendQuote?.data?.fee && onShowFeeDetail && ((0, jsx_runtime_1.jsx)("button", { onClick: onShowFeeDetail, className: "text-as-primary/40 hover:text-as-primary/60 transition-colors", children: (0, jsx_runtime_1.jsx)(lucide_react_1.Info, { className: "h-4 w-4" }) })), featureFlags.showPoints &&
86
86
  anyspendQuote?.data?.pointsAmount &&
87
87
  anyspendQuote?.data?.pointsAmount > 0 && ((0, jsx_runtime_1.jsx)(PointsBadge_1.PointsBadge, { pointsAmount: anyspendQuote.data.pointsAmount, pointsMultiplier: anyspendQuote.data.pointsMultiplier, onClick: () => onShowPointsDetail?.() }))] }), (0, jsx_runtime_1.jsxs)("div", { className: "flex flex-col items-end gap-0.5", children: [(0, jsx_runtime_1.jsxs)("span", { className: "text-as-primary font-semibold", children: ["$", getTotalAmount(selectedPaymentMethod || FiatPaymentMethod_1.FiatPaymentMethod.NONE).toFixed(2)] }), (() => {
88
88
  // For fiat payments, show the fee from the payment method
@@ -7,6 +7,7 @@ exports.B3DynamicModal = B3DynamicModal;
7
7
  const jsx_runtime_1 = require("react/jsx-runtime");
8
8
  const react_1 = require("../../../anyspend/react");
9
9
  const AnyspendDepositHype_1 = require("../../../anyspend/react/components/AnyspendDepositHype");
10
+ const AnySpendDepositUpside_1 = require("../../../anyspend/react/components/AnySpendDepositUpside");
10
11
  const AnySpendStakeUpside_1 = require("../../../anyspend/react/components/AnySpendStakeUpside");
11
12
  const AnySpendStakeUpsideExactIn_1 = require("../../../anyspend/react/components/AnySpendStakeUpsideExactIn");
12
13
  const react_2 = require("../../../global-account/react");
@@ -25,10 +26,10 @@ const NotificationsContent_1 = __importDefault(require("./ManageAccount/Notifica
25
26
  const RequestPermissions_1 = require("./RequestPermissions/RequestPermissions");
26
27
  const Send_1 = require("./Send/Send");
27
28
  const SignInWithB3Flow_1 = require("./SignInWithB3/SignInWithB3Flow");
29
+ const TurnkeyAuthModal_1 = require("./TurnkeyAuthModal");
28
30
  const index_1 = require("./Toast/index");
29
31
  const dialog_1 = require("./ui/dialog");
30
32
  const drawer_1 = require("./ui/drawer");
31
- const AnySpendDepositUpside_1 = require("../../../anyspend/react/components/AnySpendDepositUpside");
32
33
  const debug = (0, debug_1.debugB3React)("B3DynamicModal");
33
34
  function B3DynamicModal() {
34
35
  const isOpen = (0, react_2.useModalStore)(state => state.isOpen);
@@ -63,6 +64,7 @@ function B3DynamicModal() {
63
64
  "anySpendBuySpin",
64
65
  "anySpendOrderHistory",
65
66
  "signInWithB3",
67
+ "turnkeyAuth",
66
68
  "anySpendSignatureMint",
67
69
  "anySpendBondKit",
68
70
  "linkAccount",
@@ -86,10 +88,12 @@ function B3DynamicModal() {
86
88
  ];
87
89
  // Check if current content type is in freestyle types
88
90
  const isFreestyleType = freestyleTypes.includes(contentType?.type);
91
+ // Determine if modal should be closable - defaults to true unless explicitly set to false
92
+ const isClosable = contentType?.closable !== false;
89
93
  const hideCloseButton = true;
90
94
  // Build content class using cn utility
91
95
  // eslint-disable-next-line tailwindcss/no-custom-classname
92
- const contentClass = (0, cn_1.cn)("b3-modal", theme === "dark" && "dark", fullWidthTypes.includes(contentType?.type) && "w-full", isFreestyleType && "b3-modal-freestyle", contentType?.type === "signInWithB3" && "p-0", contentType?.type === "anySpend" && "md:p-0", contentType?.type === "send" && "p-0", contentType?.type === "manageAccount" && " md:p-0 md:pt-2", contentType?.type === "linkAccount" && "md:p-0");
96
+ const contentClass = (0, cn_1.cn)("b3-modal", theme === "dark" && "dark", fullWidthTypes.includes(contentType?.type) && "w-full", isFreestyleType && "b3-modal-freestyle", contentType?.type === "signInWithB3" && "p-0", contentType?.type === "turnkeyAuth" && "p-0", contentType?.type === "anySpend" && "md:p-0", contentType?.type === "send" && "p-0", contentType?.type === "manageAccount" && " md:p-0 md:pt-2", contentType?.type === "linkAccount" && "md:p-0");
93
97
  debug("contentType", contentType);
94
98
  const renderContent = () => {
95
99
  if (!contentType)
@@ -97,6 +101,8 @@ function B3DynamicModal() {
97
101
  switch (contentType.type) {
98
102
  case "signInWithB3":
99
103
  return (0, jsx_runtime_1.jsx)(SignInWithB3Flow_1.SignInWithB3Flow, { ...contentType });
104
+ case "turnkeyAuth":
105
+ return (0, jsx_runtime_1.jsx)(TurnkeyAuthModal_1.TurnkeyAuthModal, { ...contentType });
100
106
  case "requestPermissions":
101
107
  return (0, jsx_runtime_1.jsx)(RequestPermissions_1.RequestPermissions, { ...contentType });
102
108
  case "manageAccount":
@@ -154,12 +160,20 @@ function B3DynamicModal() {
154
160
  const ModalContent = isMobile ? drawer_1.DrawerContent : dialog_1.DialogContent;
155
161
  const ModalTitle = isMobile ? drawer_1.DrawerTitle : dialog_1.DialogTitle;
156
162
  const ModalDescription = isMobile ? drawer_1.DrawerDescription : dialog_1.DialogDescription;
157
- return ((0, jsx_runtime_1.jsxs)(ModalComponent, { open: isOpen, onOpenChange: setB3ModalOpen, children: [(0, jsx_runtime_1.jsxs)(ModalContent, { className: (0, cn_1.cn)(contentClass, "rounded-2xl bg-white shadow-xl dark:bg-gray-900", "border border-gray-200 dark:border-gray-800", (contentType?.type === "manageAccount" ||
163
+ // Create a wrapper for onOpenChange that respects closable property
164
+ const handleOpenChange = (open) => {
165
+ // Only allow closing if the modal is closable
166
+ if (!open && !isClosable) {
167
+ return;
168
+ }
169
+ setB3ModalOpen(open);
170
+ };
171
+ return ((0, jsx_runtime_1.jsxs)(ModalComponent, { open: isOpen, onOpenChange: handleOpenChange, children: [(0, jsx_runtime_1.jsxs)(ModalContent, { className: (0, cn_1.cn)(contentClass, "rounded-2xl bg-white shadow-xl dark:bg-gray-900", "border border-gray-200 dark:border-gray-800", (contentType?.type === "manageAccount" ||
158
172
  contentType?.type === "deposit" ||
159
173
  contentType?.type === "send" ||
160
174
  contentType?.type === "avatarEditor" ||
161
175
  contentType?.type === "notifications") &&
162
- "p-0", "mx-auto w-full max-w-md sm:max-w-lg"), hideCloseButton: hideCloseButton, children: [(0, jsx_runtime_1.jsx)(ModalTitle, { className: "sr-only hidden", children: contentType?.type || "Modal" }), (0, jsx_runtime_1.jsx)(ModalDescription, { className: "sr-only hidden", children: contentType?.type || "Modal Body" }), (0, jsx_runtime_1.jsxs)("div", { className: (0, cn_1.cn)("no-scrollbar flex max-h-[90dvh] flex-col overflow-auto sm:max-h-[80dvh]"), children: [!hideCloseButton && ((0, jsx_runtime_1.jsxs)("button", { onClick: navigateBack, className: "flex items-center gap-2 px-6 py-4 text-gray-600 transition-colors hover:text-gray-900 dark:text-gray-400 dark:hover:text-white", children: [(0, jsx_runtime_1.jsxs)("svg", { width: "20", height: "20", viewBox: "0 0 20 20", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [(0, jsx_runtime_1.jsx)("path", { d: "M15.8337 10H4.16699", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }), (0, jsx_runtime_1.jsx)("path", { d: "M10.0003 15.8334L4.16699 10L10.0003 4.16669", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" })] }), (0, jsx_runtime_1.jsx)("span", { className: "font-inter text-sm font-semibold", children: "Back" })] })), (0, jsx_runtime_1.jsx)("div", { className: "flex-1", children: renderContent() }), (0, jsx_runtime_1.jsx)(framer_motion_1.AnimatePresence, { children: toasts.length > 0 && ((0, jsx_runtime_1.jsx)(framer_motion_1.motion.div, { initial: { height: 0 }, animate: { height: "auto" }, exit: { height: 0 }, transition: { duration: 0.3, ease: "easeInOut" }, className: "toast-section relative z-10 overflow-hidden bg-white dark:border-gray-800 dark:bg-gray-900", children: (0, jsx_runtime_1.jsx)(framer_motion_1.motion.div, { initial: { opacity: 0, y: -10 }, animate: { opacity: 1, y: 0 }, exit: { opacity: 0, y: -10 }, transition: { duration: 0.2, delay: 0.1 }, className: "p-4 pt-0", children: (0, jsx_runtime_1.jsx)(index_1.ToastContainer, { toasts: toasts, onDismiss: removeToast, theme: theme }) }) })) })] })] }), isOpen && ((0, jsx_runtime_1.jsx)("style", { children: `
176
+ "p-0", "mx-auto w-full max-w-md sm:max-w-lg"), hideCloseButton: hideCloseButton, onEscapeKeyDown: !isClosable ? e => e.preventDefault() : undefined, onPointerDownOutside: !isClosable ? e => e.preventDefault() : undefined, onInteractOutside: !isClosable ? e => e.preventDefault() : undefined, children: [(0, jsx_runtime_1.jsx)(ModalTitle, { className: "sr-only hidden", children: contentType?.type || "Modal" }), (0, jsx_runtime_1.jsx)(ModalDescription, { className: "sr-only hidden", children: contentType?.type || "Modal Body" }), (0, jsx_runtime_1.jsxs)("div", { className: (0, cn_1.cn)("b3-modal-content no-scrollbar dark:bg-b3-background flex max-h-[90dvh] flex-col overflow-auto sm:max-h-[80dvh]"), children: [!hideCloseButton && ((0, jsx_runtime_1.jsxs)("button", { onClick: navigateBack, className: "flex items-center gap-2 px-6 py-4 text-gray-600 transition-colors hover:text-gray-900 dark:text-gray-400 dark:hover:text-white", children: [(0, jsx_runtime_1.jsxs)("svg", { width: "20", height: "20", viewBox: "0 0 20 20", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [(0, jsx_runtime_1.jsx)("path", { d: "M15.8337 10H4.16699", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }), (0, jsx_runtime_1.jsx)("path", { d: "M10.0003 15.8334L4.16699 10L10.0003 4.16669", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" })] }), (0, jsx_runtime_1.jsx)("span", { className: "font-inter text-sm font-semibold", children: "Back" })] })), (0, jsx_runtime_1.jsx)("div", { className: "flex-1", children: renderContent() }), (0, jsx_runtime_1.jsx)(framer_motion_1.AnimatePresence, { children: toasts.length > 0 && ((0, jsx_runtime_1.jsx)(framer_motion_1.motion.div, { initial: { height: 0 }, animate: { height: "auto" }, exit: { height: 0 }, transition: { duration: 0.3, ease: "easeInOut" }, className: "toast-section relative z-10 overflow-hidden bg-white dark:border-gray-800 dark:bg-gray-900", children: (0, jsx_runtime_1.jsx)(framer_motion_1.motion.div, { initial: { opacity: 0, y: -10 }, animate: { opacity: 1, y: 0 }, exit: { opacity: 0, y: -10 }, transition: { duration: 0.2, delay: 0.1 }, className: "p-4 pt-0", children: (0, jsx_runtime_1.jsx)(index_1.ToastContainer, { toasts: toasts, onDismiss: removeToast, theme: theme }) }) })) })] })] }), isOpen && ((0, jsx_runtime_1.jsx)("style", { children: `
163
177
  .modal-inner-content {
164
178
  transition: margin-bottom 0.3s ease-in-out;
165
179
  margin-bottom: ${toasts.length > 0 ? "0px" : "23px"} !important;
@@ -9,7 +9,7 @@ import { B3ContextType } from "./types";
9
9
  /**
10
10
  * Main B3Provider component
11
11
  */
12
- export declare function B3Provider({ theme, children, accountOverride, environment, automaticallySetFirstEoa, simDuneApiKey, toaster: _toaster, clientType, rpcUrls, partnerId, onConnect, connectors, overrideDefaultConnectors, createClientReferenceId, }: {
12
+ export declare function B3Provider({ theme, children, accountOverride, environment, automaticallySetFirstEoa, simDuneApiKey, toaster: _toaster, clientType, rpcUrls, partnerId, onConnect, connectors, overrideDefaultConnectors, createClientReferenceId, enableTurnkey, }: {
13
13
  theme: "light" | "dark";
14
14
  children: React.ReactNode;
15
15
  accountOverride?: Account;
@@ -27,11 +27,12 @@ export declare function B3Provider({ theme, children, accountOverride, environme
27
27
  connectors?: CreateConnectorFn[];
28
28
  overrideDefaultConnectors?: boolean;
29
29
  createClientReferenceId?: (params: CreateOrderParams | CreateOnrampOrderParams) => Promise<string>;
30
+ enableTurnkey?: boolean;
30
31
  }): import("react/jsx-runtime").JSX.Element;
31
32
  /**
32
33
  * Inner provider component that provides the actual B3Context
33
34
  */
34
- export declare function InnerProvider({ children, accountOverride, environment, defaultPermissions, automaticallySetFirstEoa, theme, clientType, partnerId, createClientReferenceId, }: {
35
+ export declare function InnerProvider({ children, accountOverride, environment, defaultPermissions, automaticallySetFirstEoa, theme, clientType, partnerId, createClientReferenceId, enableTurnkey, }: {
35
36
  children: React.ReactNode;
36
37
  accountOverride?: Account;
37
38
  environment: B3ContextType["environment"];
@@ -41,4 +42,5 @@ export declare function InnerProvider({ children, accountOverride, environment,
41
42
  clientType?: ClientType;
42
43
  partnerId: string;
43
44
  createClientReferenceId?: (params: CreateOrderParams | CreateOnrampOrderParams) => Promise<string>;
45
+ enableTurnkey?: boolean;
44
46
  }): import("react/jsx-runtime").JSX.Element;
@@ -34,7 +34,7 @@ const queryClient = new react_query_1.QueryClient();
34
34
  */
35
35
  function B3Provider({ theme = "light", children, accountOverride, environment, automaticallySetFirstEoa, simDuneApiKey,
36
36
  // deprecated since v0.0.87
37
- toaster: _toaster, clientType = "rest", rpcUrls, partnerId, onConnect, connectors, overrideDefaultConnectors = false, createClientReferenceId, }) {
37
+ toaster: _toaster, clientType = "rest", rpcUrls, partnerId, onConnect, connectors, overrideDefaultConnectors = false, createClientReferenceId, enableTurnkey = false, }) {
38
38
  // Initialize Google Analytics on mount
39
39
  (0, react_2.useEffect)(() => {
40
40
  (0, analytics_1.loadGA4Script)();
@@ -44,22 +44,24 @@ toaster: _toaster, clientType = "rest", rpcUrls, partnerId, onConnect, connector
44
44
  (0, client_manager_1.setClientType)(clientType);
45
45
  }, [clientType]);
46
46
  const wagmiConfig = (0, createWagmiConfig_1.createWagmiConfig)({ partnerId, rpcUrls, connectors, overrideDefaultConnectors });
47
- return ((0, jsx_runtime_1.jsx)(react_3.ThirdwebProvider, { children: (0, jsx_runtime_1.jsx)(wagmi_1.WagmiProvider, { config: wagmiConfig, reconnectOnMount: false, children: (0, jsx_runtime_1.jsx)(react_query_1.QueryClientProvider, { client: queryClient, children: (0, jsx_runtime_1.jsx)(react_1.TooltipProvider, { children: (0, jsx_runtime_1.jsx)(index_1.ToastProvider, { children: (0, jsx_runtime_1.jsx)(LocalSDKProvider_1.LocalSDKProvider, { onConnectCallback: onConnect, children: (0, jsx_runtime_1.jsxs)(InnerProvider, { accountOverride: accountOverride, environment: environment, theme: theme, automaticallySetFirstEoa: !!automaticallySetFirstEoa, clientType: clientType, partnerId: partnerId, createClientReferenceId: createClientReferenceId, children: [(0, jsx_runtime_1.jsx)(ToastContextConnector, {}), (0, jsx_runtime_1.jsxs)(react_1.RelayKitProviderWrapper, { simDuneApiKey: simDuneApiKey, children: [children, (0, jsx_runtime_1.jsx)(StyleRoot_1.StyleRoot, { id: "b3-root" })] })] }) }) }) }) }) }) }));
47
+ return ((0, jsx_runtime_1.jsx)(react_3.ThirdwebProvider, { children: (0, jsx_runtime_1.jsx)(wagmi_1.WagmiProvider, { config: wagmiConfig, reconnectOnMount: false, children: (0, jsx_runtime_1.jsx)(react_query_1.QueryClientProvider, { client: queryClient, children: (0, jsx_runtime_1.jsx)(react_1.TooltipProvider, { children: (0, jsx_runtime_1.jsx)(index_1.ToastProvider, { children: (0, jsx_runtime_1.jsx)(LocalSDKProvider_1.LocalSDKProvider, { onConnectCallback: onConnect, children: (0, jsx_runtime_1.jsxs)(InnerProvider, { accountOverride: accountOverride, environment: environment, theme: theme, automaticallySetFirstEoa: !!automaticallySetFirstEoa, clientType: clientType, partnerId: partnerId, createClientReferenceId: createClientReferenceId, enableTurnkey: enableTurnkey, children: [(0, jsx_runtime_1.jsx)(ToastContextConnector, {}), (0, jsx_runtime_1.jsxs)(react_1.RelayKitProviderWrapper, { simDuneApiKey: simDuneApiKey, children: [children, (0, jsx_runtime_1.jsx)(StyleRoot_1.StyleRoot, { id: "b3-root" })] })] }) }) }) }) }) }) }));
48
48
  }
49
49
  /**
50
50
  * Inner provider component that provides the actual B3Context
51
51
  */
52
- function InnerProvider({ children, accountOverride, environment, defaultPermissions = DEFAULT_PERMISSIONS, automaticallySetFirstEoa, theme = "light", clientType = "socket", partnerId, createClientReferenceId, }) {
52
+ function InnerProvider({ children, accountOverride, environment, defaultPermissions = DEFAULT_PERMISSIONS, automaticallySetFirstEoa, theme = "light", clientType = "socket", partnerId, createClientReferenceId, enableTurnkey, }) {
53
53
  const activeAccount = (0, react_3.useActiveAccount)();
54
54
  const [manuallySelectedWallet, setManuallySelectedWallet] = (0, react_2.useState)(undefined);
55
55
  const wallets = (0, react_3.useConnectedWallets)();
56
56
  const isAuthenticated = (0, react_1.useAuthStore)(state => state.isAuthenticated);
57
57
  const isConnected = (0, react_1.useAuthStore)(state => state.isConnected);
58
+ const justCompletedLogin = (0, react_1.useAuthStore)(state => state.justCompletedLogin);
58
59
  const setActiveWallet = (0, react_3.useSetActiveWallet)();
59
60
  const { user, setUser, refetchUser } = (0, react_1.useAuthentication)(partnerId);
60
61
  debug("@@B3Provider:isConnected", isConnected);
61
62
  debug("@@wallets", wallets);
62
63
  debug("@@B3Provider:user", user);
64
+ debug("@@B3Provider:justCompletedLogin", justCompletedLogin);
63
65
  // Use given accountOverride or activeAccount from thirdweb
64
66
  const effectiveAccount = isAuthenticated ? accountOverride || activeAccount : undefined;
65
67
  const setWallet = (0, react_2.useCallback)((wallet) => {
@@ -104,6 +106,7 @@ function InnerProvider({ children, accountOverride, environment, defaultPermissi
104
106
  clientType,
105
107
  partnerId: partnerId,
106
108
  createClientReferenceId,
109
+ enableTurnkey,
107
110
  }, children: (0, jsx_runtime_1.jsx)(InnerProvider2, { children: children }) }));
108
111
  }
109
112
  const InnerProvider2 = ({ children }) => {
@@ -23,6 +23,7 @@ export interface B3ContextType {
23
23
  clientType: ClientType;
24
24
  partnerId: string;
25
25
  createClientReferenceId?: (params: CreateOrderParams | CreateOnrampOrderParams) => Promise<string>;
26
+ enableTurnkey?: boolean;
26
27
  }
27
28
  /**
28
29
  * Context for B3 provider
@@ -20,4 +20,5 @@ exports.B3Context = (0, react_1.createContext)({
20
20
  clientType: "rest",
21
21
  partnerId: "",
22
22
  createClientReferenceId: undefined,
23
+ enableTurnkey: false,
23
24
  });
@@ -13,11 +13,11 @@ const SettingsIcon = () => {
13
13
  };
14
14
  const BottomNavigation = () => {
15
15
  const setB3ModalContentType = (0, react_1.useModalStore)(state => state.setB3ModalContentType);
16
- return ((0, jsx_runtime_1.jsx)("div", { className: "b3-modal-bottom-navigation sticky bottom-0 left-0 w-full rounded-b-xl border-t border-gray-200 bg-[#FAFAFA]", children: (0, jsx_runtime_1.jsxs)(react_1.TabsListPrimitive, { className: "flex h-[68px] w-full items-center justify-center gap-4 border-none bg-transparent", children: [(0, jsx_runtime_1.jsxs)(react_1.TabTriggerPrimitive, { value: "home", className: "data-[state=active]:border-b3-primary-blue group flex flex-initial flex-col items-center gap-1 border-r-0 border-t-0 px-6 pb-2 pt-2.5 text-[#a0a0ab] data-[state=active]:border-t-4 data-[state=active]:text-[#18181B]", children: [(0, jsx_runtime_1.jsx)(HomeIcon, {}), (0, jsx_runtime_1.jsx)("span", { className: "text-b3-grey font-neue-montreal-semibold text-xs", children: "Home" })] }), (0, jsx_runtime_1.jsxs)(react_1.TabTriggerPrimitive, { value: "swap", className: "data-[state=active]:border-b3-primary-blue group flex flex-initial flex-col items-center gap-1 border-r-0 border-t-0 px-6 pb-2 pt-2.5 text-[#a0a0ab] data-[state=active]:border-t-4 data-[state=active]:text-[#18181B]", onClick: () => {
16
+ return ((0, jsx_runtime_1.jsx)("div", { className: "b3-modal-bottom-navigation sticky bottom-0 left-0 w-full rounded-b-xl border-t border-gray-200 bg-[#FAFAFA]", children: (0, jsx_runtime_1.jsxs)(react_1.TabsListPrimitive, { className: "flex h-[68px] w-full items-center justify-center gap-4 border-none bg-transparent", children: [(0, jsx_runtime_1.jsxs)(react_1.TabTriggerPrimitive, { value: "home", className: "data-[state=active]:border-b3-primary-blue group flex flex-initial flex-col items-center gap-1 border-r-0 border-t-0 px-6 pb-2 pt-2.5 text-[#a0a0ab] data-[state=active]:border-t-4 data-[state=active]:text-[#18181B] dark:data-[state=active]:text-white", children: [(0, jsx_runtime_1.jsx)(HomeIcon, {}), (0, jsx_runtime_1.jsx)("span", { className: "text-b3-grey font-neue-montreal-semibold text-xs", children: "Home" })] }), (0, jsx_runtime_1.jsxs)(react_1.TabTriggerPrimitive, { value: "swap", className: "data-[state=active]:border-b3-primary-blue group flex flex-initial flex-col items-center gap-1 border-r-0 border-t-0 px-6 pb-2 pt-2.5 text-[#a0a0ab] data-[state=active]:border-t-4 data-[state=active]:text-[#18181B] dark:data-[state=active]:text-white", onClick: () => {
17
17
  setB3ModalContentType({
18
18
  type: "anySpend",
19
19
  showBackButton: true,
20
20
  });
21
- }, children: [(0, jsx_runtime_1.jsx)(SwapIcon, {}), (0, jsx_runtime_1.jsx)("span", { className: "text-b3-grey font-neue-montreal-semibold text-xs", children: "Swap" })] }), (0, jsx_runtime_1.jsxs)(react_1.TabTriggerPrimitive, { value: "settings", className: "data-[state=active]:border-b3-primary-blue group flex flex-initial flex-col items-center gap-1 border-r-0 border-t-0 px-6 pb-2 pt-2.5 text-[#a0a0ab] data-[state=active]:border-t-4 data-[state=active]:text-[#18181B]", children: [(0, jsx_runtime_1.jsx)(SettingsIcon, {}), (0, jsx_runtime_1.jsx)("span", { className: "text-b3-grey font-neue-montreal-semibold text-xs", children: "Settings" })] })] }) }));
21
+ }, children: [(0, jsx_runtime_1.jsx)(SwapIcon, {}), (0, jsx_runtime_1.jsx)("span", { className: "text-b3-grey font-neue-montreal-semibold text-xs", children: "Swap" })] }), (0, jsx_runtime_1.jsxs)(react_1.TabTriggerPrimitive, { value: "settings", className: "data-[state=active]:border-b3-primary-blue group flex flex-initial flex-col items-center gap-1 border-r-0 border-t-0 px-6 pb-2 pt-2.5 text-[#a0a0ab] data-[state=active]:border-t-4 data-[state=active]:text-[#18181B] dark:data-[state=active]:text-white", children: [(0, jsx_runtime_1.jsx)(SettingsIcon, {}), (0, jsx_runtime_1.jsx)("span", { className: "text-b3-grey font-neue-montreal-semibold text-xs", children: "Settings" })] })] }) }));
22
22
  };
23
23
  exports.default = BottomNavigation;
@@ -16,16 +16,18 @@ const MAX_REFETCH_ATTEMPTS = 20;
16
16
  * Handles different login providers, authentication steps, and session key management
17
17
  */
18
18
  function SignInWithB3Flow({ strategies, onLoginSuccess, onSessionKeySuccess, onError, chain, sessionKeyAddress, partnerId, closeAfterLogin = false, source = "signInWithB3Button", signersEnabled = false, }) {
19
- const { automaticallySetFirstEoa } = (0, react_1.useB3)();
19
+ const { automaticallySetFirstEoa, user, refetchUser, enableTurnkey } = (0, react_1.useB3)();
20
20
  const [step, setStep] = (0, react_2.useState)(source === "requestPermissions" ? null : "login");
21
21
  const [sessionKeyAdded, setSessionKeyAdded] = (0, react_2.useState)(source === "requestPermissions" ? true : false);
22
- const { setB3ModalContentType, setB3ModalOpen, isOpen } = (0, react_1.useModalStore)();
22
+ const { setB3ModalContentType, setB3ModalOpen, isOpen, contentType } = (0, react_1.useModalStore)();
23
23
  const account = (0, react_3.useActiveAccount)();
24
24
  const isAuthenticating = (0, react_1.useAuthStore)(state => state.isAuthenticating);
25
25
  const isAuthenticated = (0, react_1.useAuthStore)(state => state.isAuthenticated);
26
26
  const isConnected = (0, react_1.useAuthStore)(state => state.isConnected);
27
+ const setJustCompletedLogin = (0, react_1.useAuthStore)(state => state.setJustCompletedLogin);
27
28
  const [refetchCount, setRefetchCount] = (0, react_2.useState)(0);
28
29
  const [refetchError, setRefetchError] = (0, react_2.useState)(null);
30
+ const [turnkeyAuthCompleted, setTurnkeyAuthCompleted] = (0, react_2.useState)(false);
29
31
  const { data: signers, refetch: refetchSigners, isFetching: isFetchingSigners, } = (0, react_1.useGetAllTWSigners)({
30
32
  chain,
31
33
  accountAddress: account?.address,
@@ -55,6 +57,104 @@ function SignInWithB3Flow({ strategies, onLoginSuccess, onSessionKeySuccess, onE
55
57
  setRefetchQueued(false);
56
58
  }, backoffDelay);
57
59
  }, [refetchCount, refetchSigners, onError, setRefetchQueued, refetchQueued]);
60
+ // Extract the completion flow logic to be reused
61
+ const handlePostTurnkeyFlow = (0, react_2.useCallback)(() => {
62
+ debug("Running post-Turnkey flow logic");
63
+ // Check if we already have a signer for this partner
64
+ const hasExistingSigner = signers?.some(signer => signer.partner.id === partnerId);
65
+ if (hasExistingSigner) {
66
+ // Path 1: User already has a signer for this partner
67
+ setSessionKeyAdded(true);
68
+ onSessionKeySuccess?.();
69
+ if (closeAfterLogin) {
70
+ setB3ModalOpen(false);
71
+ }
72
+ else {
73
+ setB3ModalContentType({
74
+ type: "manageAccount",
75
+ chain,
76
+ partnerId,
77
+ });
78
+ }
79
+ }
80
+ else if (signersEnabled) {
81
+ // Path 2: No existing signer, but signers are enabled
82
+ if (source !== "requestPermissions") {
83
+ // Navigate to permissions step to request new signer
84
+ setStep("permissions");
85
+ }
86
+ else {
87
+ // Already in request permissions flow, retry fetching signers
88
+ handleRefetchSigners();
89
+ }
90
+ }
91
+ else {
92
+ // Path 3: No existing signer and signers are not enabled
93
+ // Default handling for when no signer exists and signers are not enabled
94
+ if (closeAfterLogin) {
95
+ setB3ModalOpen(false);
96
+ }
97
+ else {
98
+ // if not closed, default to manage account
99
+ setB3ModalContentType({
100
+ type: "manageAccount",
101
+ chain,
102
+ partnerId,
103
+ });
104
+ }
105
+ }
106
+ }, [
107
+ signers,
108
+ partnerId,
109
+ onSessionKeySuccess,
110
+ closeAfterLogin,
111
+ setB3ModalOpen,
112
+ setB3ModalContentType,
113
+ chain,
114
+ source,
115
+ signersEnabled,
116
+ handleRefetchSigners,
117
+ setSessionKeyAdded,
118
+ ]);
119
+ // Define handleTurnkeySuccess before the useEffect that uses it
120
+ const handleTurnkeySuccess = (0, react_2.useCallback)(async (user) => {
121
+ debug("Turnkey authentication successful - setting completed flag", { user });
122
+ // Set completed flag FIRST before any async operations
123
+ setTurnkeyAuthCompleted(true);
124
+ // Refetch user to update the user state with Turnkey ID
125
+ debug("Refetching user after Turnkey success...");
126
+ await refetchUser();
127
+ debug("User refetched successfully");
128
+ // After user data is refreshed, close Turnkey modal and go back to sign-in flow
129
+ debug("Switching back to signInWithB3 modal");
130
+ setB3ModalContentType({
131
+ type: "signInWithB3",
132
+ strategies,
133
+ onLoginSuccess,
134
+ onSessionKeySuccess,
135
+ onError,
136
+ chain,
137
+ sessionKeyAddress,
138
+ partnerId,
139
+ closeAfterLogin,
140
+ source,
141
+ signersEnabled,
142
+ });
143
+ // The useEffect will re-run with updated user data to complete the sign-in process
144
+ }, [
145
+ refetchUser,
146
+ setB3ModalContentType,
147
+ strategies,
148
+ onLoginSuccess,
149
+ onSessionKeySuccess,
150
+ onError,
151
+ chain,
152
+ sessionKeyAddress,
153
+ partnerId,
154
+ closeAfterLogin,
155
+ source,
156
+ signersEnabled,
157
+ ]);
58
158
  // Handle post-login flow after signers are loaded
59
159
  (0, react_2.useEffect)(() => {
60
160
  debug("@@SignInWithB3Flow:useEffect", {
@@ -65,41 +165,46 @@ function SignInWithB3Flow({ strategies, onLoginSuccess, onSessionKeySuccess, onE
65
165
  isOpen,
66
166
  source,
67
167
  });
68
- if (isConnected && isAuthenticated) {
69
- // Check if we already have a signer for this partner
70
- const hasExistingSigner = signers?.some(signer => signer.partner.id === partnerId);
71
- if (hasExistingSigner) {
72
- setSessionKeyAdded(true);
73
- onSessionKeySuccess?.();
74
- if (closeAfterLogin) {
75
- setB3ModalOpen(false);
76
- }
77
- else {
78
- setB3ModalContentType({
79
- type: "manageAccount",
80
- chain,
81
- partnerId,
82
- });
83
- }
84
- }
85
- else if (source !== "requestPermissions") {
86
- if (signersEnabled)
87
- setStep("permissions");
88
- }
89
- else {
90
- if (signersEnabled)
91
- handleRefetchSigners();
92
- }
93
- // Default handling
168
+ if (isConnected && isAuthenticated && user) {
169
+ // Mark that login just completed BEFORE opening manage account or closing modal
170
+ // This allows Turnkey modal to show (if enableTurnkey is true)
94
171
  if (closeAfterLogin) {
95
- setB3ModalOpen(false);
172
+ setJustCompletedLogin(true);
96
173
  }
97
- // if not closed, always default to manage account
98
- setB3ModalContentType({
99
- type: "manageAccount",
100
- chain,
101
- partnerId,
102
- });
174
+ // Check if we should show Turnkey login form
175
+ // Show if enableTurnkey is true AND user just logged in AND hasn't completed Turnkey auth in this session
176
+ // For new users (!turnkeyId): Show email form
177
+ // For returning users (turnkeyId && turnkeyEmail): Auto-skip to OTP
178
+ // Also check that we're not already showing the Turnkey modal
179
+ const hasTurnkeyId = user?.partnerIds?.turnkeyId;
180
+ const hasTurnkeyEmail = !!user?.email;
181
+ const isTurnkeyModalCurrentlyOpen = contentType?.type === "turnkeyAuth";
182
+ const shouldShowTurnkeyModal = enableTurnkey &&
183
+ user &&
184
+ !turnkeyAuthCompleted &&
185
+ !isTurnkeyModalCurrentlyOpen &&
186
+ (!hasTurnkeyId || (hasTurnkeyId && hasTurnkeyEmail));
187
+ if (shouldShowTurnkeyModal) {
188
+ // Extract email from user object - check partnerIds.turnkeyEmail first, then twProfiles, then user.email
189
+ const email = user?.email || user?.twProfiles?.find((profile) => profile.details?.email)?.details?.email;
190
+ // Open Turnkey modal through the modal store
191
+ setB3ModalContentType({
192
+ type: "turnkeyAuth",
193
+ onSuccess: handleTurnkeySuccess,
194
+ onClose: () => {
195
+ // After closing Turnkey modal, continue with the rest of the flow
196
+ setTurnkeyAuthCompleted(true);
197
+ debug("Turnkey modal closed, running post-Turnkey flow");
198
+ handlePostTurnkeyFlow();
199
+ },
200
+ initialEmail: email,
201
+ skipToOtp: !!(hasTurnkeyId && hasTurnkeyEmail),
202
+ closable: false, // Turnkey modal cannot be closed until auth is complete
203
+ });
204
+ return;
205
+ }
206
+ // Normal flow continues after Turnkey auth is complete (or if not needed)
207
+ handlePostTurnkeyFlow();
103
208
  }
104
209
  }, [
105
210
  signers,
@@ -117,6 +222,13 @@ function SignInWithB3Flow({ strategies, onLoginSuccess, onSessionKeySuccess, onE
117
222
  isAuthenticating,
118
223
  isAuthenticated,
119
224
  isOpen,
225
+ setJustCompletedLogin,
226
+ user,
227
+ enableTurnkey,
228
+ turnkeyAuthCompleted,
229
+ handleTurnkeySuccess,
230
+ contentType,
231
+ handlePostTurnkeyFlow,
120
232
  ]);
121
233
  debug("render", {
122
234
  step,
@@ -171,24 +283,28 @@ function SignInWithB3Flow({ strategies, onLoginSuccess, onSessionKeySuccess, onE
171
283
  });
172
284
  }
173
285
  }, [chain, onError, onSessionKeySuccessEnhanced, sessionKeyAddress, setB3ModalContentType, step]);
286
+ // Render content based on current step/state
287
+ let content = null;
174
288
  // Display error if refetch limit exceeded
175
289
  if (refetchError) {
176
- return ((0, jsx_runtime_1.jsx)(LoginStep_1.LoginStepContainer, { partnerId: partnerId, children: (0, jsx_runtime_1.jsx)("div", { className: "p-4 text-center text-red-500", children: refetchError }) }));
290
+ content = ((0, jsx_runtime_1.jsx)(LoginStep_1.LoginStepContainer, { partnerId: partnerId, children: (0, jsx_runtime_1.jsx)("div", { className: "p-4 text-center text-red-500", children: refetchError }) }));
177
291
  }
178
- if (isAuthenticating || (isFetchingSigners && step === "login") || source === "requestPermissions") {
179
- return ((0, jsx_runtime_1.jsx)(LoginStep_1.LoginStepContainer, { partnerId: partnerId, children: (0, jsx_runtime_1.jsx)("div", { className: "my-8 flex min-h-[350px] items-center justify-center", children: (0, jsx_runtime_1.jsx)(react_1.Loading, { variant: "white", size: "lg" }) }) }));
292
+ else if (isAuthenticating || (isFetchingSigners && step === "login") || source === "requestPermissions") {
293
+ content = ((0, jsx_runtime_1.jsx)(LoginStep_1.LoginStepContainer, { partnerId: partnerId, children: (0, jsx_runtime_1.jsx)("div", { className: "my-8 flex min-h-[350px] items-center justify-center", children: (0, jsx_runtime_1.jsx)(react_1.Loading, { variant: "white", size: "lg" }) }) }));
180
294
  }
181
- if (step === "login") {
295
+ else if (step === "login") {
182
296
  // Custom strategy
183
297
  if (strategies?.[0] === "privy") {
184
- return (0, jsx_runtime_1.jsx)(SignInWithB3Privy_1.SignInWithB3Privy, { onSuccess: handleLoginSuccess, chain: chain });
298
+ content = (0, jsx_runtime_1.jsx)(SignInWithB3Privy_1.SignInWithB3Privy, { onSuccess: handleLoginSuccess, chain: chain });
299
+ }
300
+ else if (strategies) {
301
+ // Strategies are explicitly provided
302
+ content = ((0, jsx_runtime_1.jsx)(LoginStepCustom_1.LoginStepCustom, { strategies: strategies, chain: chain, onSuccess: handleLoginSuccess, onError: onError, automaticallySetFirstEoa: !!automaticallySetFirstEoa }));
185
303
  }
186
- // Strategies are explicitly provided
187
- if (strategies) {
188
- return ((0, jsx_runtime_1.jsx)(LoginStepCustom_1.LoginStepCustom, { strategies: strategies, chain: chain, onSuccess: handleLoginSuccess, onError: onError, automaticallySetFirstEoa: !!automaticallySetFirstEoa }));
304
+ else {
305
+ // Default to handle all strategies we support
306
+ content = (0, jsx_runtime_1.jsx)(LoginStep_1.LoginStep, { chain: chain, onSuccess: handleLoginSuccess, onError: onError });
189
307
  }
190
- // Default to handle all strategies we support
191
- return (0, jsx_runtime_1.jsx)(LoginStep_1.LoginStep, { chain: chain, onSuccess: handleLoginSuccess, onError: onError });
192
308
  }
193
- return null;
309
+ return content;
194
310
  }
@@ -0,0 +1,8 @@
1
+ interface TurnkeyAuthModalProps {
2
+ onClose: () => void;
3
+ onSuccess: (_user: any) => void;
4
+ initialEmail?: string;
5
+ skipToOtp?: boolean;
6
+ }
7
+ export declare function TurnkeyAuthModal({ onClose, onSuccess, initialEmail, skipToOtp }: TurnkeyAuthModalProps): import("react/jsx-runtime").JSX.Element;
8
+ export {};
@@ -0,0 +1,84 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TurnkeyAuthModal = TurnkeyAuthModal;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const react_1 = require("react");
6
+ const useTurnkeyAuth_1 = require("../hooks/useTurnkeyAuth");
7
+ function TurnkeyAuthModal({ onClose, onSuccess, initialEmail = "", skipToOtp = false }) {
8
+ const [step, setStep] = (0, react_1.useState)(skipToOtp ? "otp" : "email");
9
+ const [email, setEmail] = (0, react_1.useState)(initialEmail);
10
+ const [otpCode, setOtpCode] = (0, react_1.useState)("");
11
+ const [otpId, setOtpId] = (0, react_1.useState)("");
12
+ const autoSubmitTriggeredRef = (0, react_1.useRef)(false);
13
+ const { initiateLogin, verifyOtp, isLoading, error, clearError } = (0, useTurnkeyAuth_1.useTurnkeyAuth)();
14
+ // Update email when initialEmail changes
15
+ (0, react_1.useEffect)(() => {
16
+ if (initialEmail && initialEmail !== email) {
17
+ setEmail(initialEmail);
18
+ }
19
+ }, [initialEmail, email]);
20
+ // Auto-submit email form if skipToOtp is true - triggers on mount when skipToOtp=true
21
+ (0, react_1.useEffect)(() => {
22
+ if (skipToOtp && email && step === "otp" && !otpId && !isLoading && !autoSubmitTriggeredRef.current) {
23
+ autoSubmitTriggeredRef.current = true;
24
+ // Call initiateLogin directly to get OTP
25
+ initiateLogin(email)
26
+ .then(result => {
27
+ setOtpId(result.otpId);
28
+ })
29
+ .catch(err => {
30
+ console.error("Failed to initiate login:", err);
31
+ });
32
+ }
33
+ // eslint-disable-next-line react-hooks/exhaustive-deps
34
+ }, [skipToOtp, email, step, otpId, isLoading]);
35
+ const handleEmailSubmit = async (e) => {
36
+ e.preventDefault();
37
+ try {
38
+ const result = await initiateLogin(email);
39
+ setOtpId(result.otpId);
40
+ setStep("otp");
41
+ }
42
+ catch (err) {
43
+ // Error is handled by the hook
44
+ console.error("Failed to initiate login:", err);
45
+ }
46
+ };
47
+ const handleOtpSubmit = async (e) => {
48
+ e.preventDefault();
49
+ try {
50
+ const result = await verifyOtp(otpId, otpCode);
51
+ setStep("success");
52
+ // Auto-close after success and notify parent
53
+ setTimeout(() => {
54
+ onSuccess(result.user);
55
+ handleClose();
56
+ }, 1500);
57
+ }
58
+ catch (err) {
59
+ // Error is handled by the hook
60
+ console.error("Failed to verify OTP:", err);
61
+ }
62
+ };
63
+ const handleClose = () => {
64
+ // Reset state
65
+ setStep("email");
66
+ setEmail("");
67
+ setOtpCode("");
68
+ setOtpId("");
69
+ autoSubmitTriggeredRef.current = false;
70
+ clearError();
71
+ onClose();
72
+ };
73
+ const handleResendOtp = async () => {
74
+ try {
75
+ const result = await initiateLogin(email);
76
+ setOtpId(result.otpId);
77
+ clearError();
78
+ }
79
+ catch (err) {
80
+ console.error("Failed to resend OTP:", err);
81
+ }
82
+ };
83
+ return ((0, jsx_runtime_1.jsxs)("div", { className: "font-neue-montreal p-8", children: [step === "email" && ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("h2", { className: "mb-6 text-center text-2xl font-bold text-gray-900 dark:text-white", children: "Setup your AnySpend Wallet" }), (0, jsx_runtime_1.jsxs)("div", { className: "mb-6 space-y-3 text-center text-sm text-gray-600 dark:text-gray-400", children: [(0, jsx_runtime_1.jsxs)("p", { children: ["AnySpend uses a secure,", (0, jsx_runtime_1.jsx)("br", {}), "embedded wallet to fund your workflows."] }), (0, jsx_runtime_1.jsxs)("p", { children: ["Please provide an email address to secure", (0, jsx_runtime_1.jsx)("br", {}), "your wallet."] })] }), (0, jsx_runtime_1.jsxs)("form", { onSubmit: handleEmailSubmit, className: "space-y-4", children: [(0, jsx_runtime_1.jsx)("div", { children: (0, jsx_runtime_1.jsx)("input", { type: "email", placeholder: "email", value: email, onChange: e => setEmail(e.target.value), required: true, disabled: isLoading, className: "w-full rounded-lg border border-gray-300 px-4 py-3 text-center text-gray-900 placeholder-gray-400 focus:border-blue-500 focus:outline-none focus:ring-2 focus:ring-blue-500/20 disabled:cursor-not-allowed disabled:bg-gray-50 dark:border-gray-700 dark:bg-gray-800 dark:text-white dark:placeholder-gray-500" }) }), error && ((0, jsx_runtime_1.jsx)("div", { className: "rounded-md bg-red-50 p-3 text-sm text-red-800 dark:bg-red-900/20 dark:text-red-400", children: error })), (0, jsx_runtime_1.jsx)("button", { type: "submit", disabled: isLoading || !email, className: "w-full rounded-lg bg-blue-600 px-6 py-3 font-semibold text-white transition-all duration-200 hover:bg-blue-700 disabled:cursor-not-allowed disabled:bg-gray-300 dark:disabled:bg-gray-700", children: isLoading ? ((0, jsx_runtime_1.jsxs)("span", { className: "flex items-center justify-center gap-2", children: [(0, jsx_runtime_1.jsx)("div", { className: "h-4 w-4 animate-spin rounded-full border-2 border-white border-t-transparent" }), "Sending..."] })) : ("Continue") })] })] })), step === "otp" && ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("h2", { className: "mb-4 text-center text-2xl font-bold text-gray-900 dark:text-white", children: "2FA Security" }), (0, jsx_runtime_1.jsx)("div", { className: "mb-6 space-y-3 text-center text-sm text-gray-600 dark:text-gray-400", children: (0, jsx_runtime_1.jsxs)("p", { children: ["AnySpend uses a secure,", (0, jsx_runtime_1.jsx)("br", {}), "embedded wallet to fund your workflows.", (0, jsx_runtime_1.jsx)("br", {}), "Please provide 2FA code sent to your email."] }) }), (0, jsx_runtime_1.jsxs)("form", { onSubmit: handleOtpSubmit, className: "space-y-4", children: [(0, jsx_runtime_1.jsx)("div", { children: (0, jsx_runtime_1.jsx)("input", { type: "text", placeholder: "Enter code", value: otpCode, onChange: e => setOtpCode(e.target.value.toUpperCase()), required: true, disabled: isLoading, autoFocus: true, className: "w-full rounded-lg border border-gray-300 px-4 py-3 text-center font-mono text-lg uppercase tracking-wider text-gray-900 placeholder-gray-400 focus:border-blue-500 focus:outline-none focus:ring-2 focus:ring-blue-500/20 disabled:cursor-not-allowed disabled:bg-gray-50 dark:border-gray-700 dark:bg-gray-800 dark:text-white dark:placeholder-gray-500", maxLength: 20 }) }), error && ((0, jsx_runtime_1.jsx)("div", { className: "rounded-md bg-red-50 p-3 text-sm text-red-800 dark:bg-red-900/20 dark:text-red-400", children: error })), (0, jsx_runtime_1.jsxs)("div", { className: "flex flex-col gap-2", children: [(0, jsx_runtime_1.jsx)("button", { type: "submit", disabled: isLoading || !otpCode, className: "w-full rounded-lg bg-blue-600 px-6 py-3 font-semibold text-white transition-all duration-200 hover:bg-blue-700 disabled:cursor-not-allowed disabled:bg-gray-300 dark:disabled:bg-gray-700", children: isLoading ? ((0, jsx_runtime_1.jsxs)("span", { className: "flex items-center justify-center gap-2", children: [(0, jsx_runtime_1.jsx)("div", { className: "h-4 w-4 animate-spin rounded-full border-2 border-white border-t-transparent" }), "Verifying..."] })) : ("Confirm") }), (0, jsx_runtime_1.jsx)("button", { type: "button", onClick: handleResendOtp, disabled: isLoading, className: "text-sm text-blue-600 hover:text-blue-700 hover:underline disabled:cursor-not-allowed disabled:text-gray-400 dark:text-blue-400 dark:hover:text-blue-300", children: "Resend code" })] })] })] })), step === "success" && ((0, jsx_runtime_1.jsxs)("div", { className: "text-center", children: [(0, jsx_runtime_1.jsx)("div", { className: "mb-6 flex items-center justify-center", children: (0, jsx_runtime_1.jsx)("div", { className: "flex h-16 w-16 items-center justify-center rounded-full bg-green-100 dark:bg-green-900/20", children: (0, jsx_runtime_1.jsx)("svg", { className: "h-8 w-8 text-green-600 dark:text-green-400", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: (0, jsx_runtime_1.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M5 13l4 4L19 7" }) }) }) }), (0, jsx_runtime_1.jsx)("h2", { className: "mb-2 text-2xl font-bold text-gray-900 dark:text-white", children: "Successfully Authenticated!" }), (0, jsx_runtime_1.jsx)("p", { className: "text-sm text-gray-600 dark:text-gray-400", children: "Redirecting..." })] }))] }));
84
+ }
@@ -12,6 +12,7 @@ export { SignInWithB3Flow } from "./SignInWithB3/SignInWithB3Flow";
12
12
  export { SignInWithB3Privy } from "./SignInWithB3/SignInWithB3Privy";
13
13
  export { LoginStepContainer } from "./SignInWithB3/steps/LoginStep";
14
14
  export { getConnectOptionsFromStrategy, isWalletType, type AllowedStrategy } from "./SignInWithB3/utils/signInUtils";
15
+ export { TurnkeyAuthModal } from "./TurnkeyAuthModal";
15
16
  export { ManageAccount } from "./ManageAccount/ManageAccount";
16
17
  export { Deposit } from "./Deposit/Deposit";
17
18
  export { Send } from "./Send/Send";