@b3dotfun/sdk 0.1.69-alpha.24 → 0.1.69-alpha.25

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 (31) hide show
  1. package/dist/cjs/global-account/react/components/AvatarEditor/AvatarEditor.js +3 -1
  2. package/dist/cjs/global-account/react/components/ManageAccount/BottomNavigation.js +4 -2
  3. package/dist/cjs/global-account/react/components/ManageAccount/Header.js +37 -4
  4. package/dist/cjs/global-account/react/components/ManageAccount/HomeContent.js +4 -1
  5. package/dist/cjs/global-account/react/components/ManageAccount/ProfileSection.js +5 -3
  6. package/dist/cjs/global-account/react/components/ManageAccount/SettingsContent.js +3 -1
  7. package/dist/cjs/global-account/react/components/ManageAccount/SettingsProfileCard.js +25 -14
  8. package/dist/cjs/global-account/react/components/SignInWithB3/SignIn.js +14 -4
  9. package/dist/cjs/shared/constants/index.d.ts +1 -0
  10. package/dist/cjs/shared/constants/index.js +2 -1
  11. package/dist/esm/global-account/react/components/AvatarEditor/AvatarEditor.js +3 -1
  12. package/dist/esm/global-account/react/components/ManageAccount/BottomNavigation.js +5 -3
  13. package/dist/esm/global-account/react/components/ManageAccount/Header.js +38 -5
  14. package/dist/esm/global-account/react/components/ManageAccount/HomeContent.js +4 -1
  15. package/dist/esm/global-account/react/components/ManageAccount/ProfileSection.js +6 -4
  16. package/dist/esm/global-account/react/components/ManageAccount/SettingsContent.js +4 -2
  17. package/dist/esm/global-account/react/components/ManageAccount/SettingsProfileCard.js +25 -14
  18. package/dist/esm/global-account/react/components/SignInWithB3/SignIn.js +16 -6
  19. package/dist/esm/shared/constants/index.d.ts +1 -0
  20. package/dist/esm/shared/constants/index.js +1 -0
  21. package/dist/types/shared/constants/index.d.ts +1 -0
  22. package/package.json +1 -1
  23. package/src/global-account/react/components/AvatarEditor/AvatarEditor.tsx +3 -1
  24. package/src/global-account/react/components/ManageAccount/BottomNavigation.tsx +18 -14
  25. package/src/global-account/react/components/ManageAccount/Header.tsx +74 -4
  26. package/src/global-account/react/components/ManageAccount/HomeContent.tsx +25 -19
  27. package/src/global-account/react/components/ManageAccount/ProfileSection.tsx +14 -7
  28. package/src/global-account/react/components/ManageAccount/SettingsContent.tsx +18 -14
  29. package/src/global-account/react/components/ManageAccount/SettingsProfileCard.tsx +29 -20
  30. package/src/global-account/react/components/SignInWithB3/SignIn.tsx +43 -14
  31. package/src/shared/constants/index.ts +2 -0
@@ -16,7 +16,8 @@ const SettingsProfileCard = () => {
16
16
  address: eoaAddress || account?.address,
17
17
  fresh: true,
18
18
  });
19
- const { partnerId } = useB3Config();
19
+ const { partnerId, authStrategy } = useB3Config();
20
+ const isBetterAuth = authStrategy === "better-auth";
20
21
  const { user, setUser } = useAuthentication(partnerId);
21
22
  const setB3ModalOpen = useModalStore(state => state.setB3ModalOpen);
22
23
  const setB3ModalContentType = useModalStore(state => state.setB3ModalContentType);
@@ -66,19 +67,29 @@ const SettingsProfileCard = () => {
66
67
  }
67
68
  setIsSaving(true);
68
69
  try {
69
- const sanitizedUsername = ens_normalize(editedUsername.trim());
70
- const b3Username = `${sanitizedUsername}.b3.fun`;
71
- const usernameSignMessage = `Register "${b3Username}"`;
72
- const usernameSignature = await account?.signMessage({ message: usernameSignMessage });
73
- if (!usernameSignature) {
74
- throw new Error("Failed to sign username registration message");
70
+ let updatedUser;
71
+ if (isBetterAuth) {
72
+ // Better Auth: register username without wallet signing (DB-only, no ENS)
73
+ // Skip ens_normalize it rejects underscores/mixed-case that are valid non-ENS usernames
74
+ const sanitizedUsername = editedUsername.trim().toLowerCase();
75
+ // Type assertion needed: b3-mono now accepts message/hash as optional for Better Auth users
76
+ updatedUser = (await app
77
+ .service("users")
78
+ .registerUsername({ username: sanitizedUsername }, {}));
79
+ }
80
+ else {
81
+ // Thirdweb: register username with wallet signature + on-chain ENS
82
+ const sanitizedUsername = ens_normalize(editedUsername.trim());
83
+ const b3Username = `${sanitizedUsername}.b3.fun`;
84
+ const usernameSignMessage = `Register "${b3Username}"`;
85
+ const usernameSignature = await account?.signMessage({ message: usernameSignMessage });
86
+ if (!usernameSignature) {
87
+ throw new Error("Failed to sign username registration message");
88
+ }
89
+ updatedUser = (await app
90
+ .service("users")
91
+ .registerUsername({ username: sanitizedUsername, message: usernameSignMessage, hash: usernameSignature }, {}));
75
92
  }
76
- console.log("@@usernameSignature", usernameSignature);
77
- // Register username with ENS
78
- // Note: Type assertion needed until @b3dotfun/b3-api package is updated with RegisterUsername type
79
- const updatedUser = (await app
80
- .service("users")
81
- .registerUsername({ username: sanitizedUsername, message: usernameSignMessage, hash: usernameSignature }, {}));
82
93
  // Update user state - registerUsername returns an array with single user
83
94
  setUser(Array.isArray(updatedUser) ? updatedUser[0] : updatedUser);
84
95
  // Refresh profile to get updated data
@@ -107,6 +118,6 @@ const SettingsProfileCard = () => {
107
118
  /* Edit mode - inline input */
108
119
  _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("input", { ref: inputRef, type: "text", value: editedUsername, onChange: e => setEditedUsername(e.target.value), onKeyDown: handleKeyDown, disabled: isSaving, className: "border-b3-line bg-b3-background text-b3-grey placeholder:text-b3-foreground-muted font-neue-montreal-medium focus:border-b3-primary-blue text-md w-full rounded-md border px-2 py-1 leading-none transition-colors focus:outline-none disabled:opacity-50", placeholder: "Enter username" }), _jsxs("div", { className: "flex items-center gap-1", children: [_jsx("button", { onClick: handleSaveUsername, disabled: isSaving, className: "text-b3-primary-blue hover:text-b3-primary-blue/80 flex items-center justify-center rounded-md p-1 transition-colors disabled:opacity-50", "aria-label": "Save username", children: isSaving ? _jsx(Loader2, { size: 18, className: "animate-spin" }) : _jsx(Check, { size: 18, strokeWidth: 2.5 }) }), _jsx("button", { onClick: handleCancelEdit, disabled: isSaving, className: "text-b3-foreground-muted hover:text-b3-grey flex items-center justify-center rounded-md p-1 transition-colors disabled:opacity-50", "aria-label": "Cancel editing", children: _jsx(X, { size: 18, strokeWidth: 2.5 }) })] })] })) : (
109
120
  /* Display mode */
110
- _jsxs(_Fragment, { children: [_jsx("div", { className: "flex items-center gap-1", children: _jsx("p", { className: "b3-modal-username font-neue-montreal-semibold text-lg leading-none text-[#0B57C2]", children: currentUsername }) }), _jsx("button", { onClick: handleEditUsername, className: "flex items-center justify-center gap-1 text-left transition-opacity hover:opacity-80", children: _jsx("p", { className: "font-inter text-sm font-semibold leading-5 text-[#51525C] dark:text-white", children: "Edit Username" }) })] })) })] }));
121
+ _jsxs(_Fragment, { children: [_jsx("div", { className: "flex items-center gap-1", children: _jsx("p", { className: "b3-modal-username font-neue-montreal-semibold text-lg leading-none text-[#0B57C2]", children: currentUsername || user?.email }) }), _jsx("button", { onClick: handleEditUsername, className: "flex items-center justify-center gap-1 text-left transition-opacity hover:opacity-80", children: _jsx("p", { className: "font-inter text-sm font-semibold leading-5 text-[#51525C] dark:text-white", children: "Edit Username" }) })] })) })] }));
111
122
  };
112
123
  export default SettingsProfileCard;
@@ -1,15 +1,18 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { IPFSMediaRenderer, SignInWithB3, StyleRoot, useAccountWallet, useAuthentication, useB3Config, useIsMobile, } from "../../../../global-account/react/index.js";
2
+ import { IPFSMediaRenderer, SignInWithB3, StyleRoot, useAccountWallet, useAuthStore, useAuthentication, useB3Config, useIsMobile, } from "../../../../global-account/react/index.js";
3
3
  import Icon from "../../../../global-account/react/components/custom/Icon.js";
4
- import { ecosystemWalletId } from "../../../../shared/constants/index.js";
4
+ import { AVATAR_COLORS, ecosystemWalletId } from "../../../../shared/constants/index.js";
5
5
  import { cn, truncateAddress } from "../../../../shared/utils/index.js";
6
6
  import { Menu, MenuButton, MenuItems, Transition } from "@headlessui/react";
7
+ import Avatar from "boring-avatars";
7
8
  import { useEffect } from "react";
8
9
  import { useConnectedWallets, useSetActiveWallet, useWalletImage } from "thirdweb/react";
10
+ import { useUser } from "../../hooks/useUser.js";
9
11
  import { ManageAccountButton } from "../custom/ManageAccountButton.js";
10
12
  export function SignIn(props) {
11
13
  const { className } = props;
12
- const { automaticallySetFirstEoa, partnerId } = useB3Config();
14
+ const { automaticallySetFirstEoa, partnerId, authStrategy } = useB3Config();
15
+ const isBetterAuth = authStrategy === "better-auth";
13
16
  const { address: globalAddress, ensName, connectedSmartWallet, connectedEOAWallet, isActiveSmartWallet, isActiveEOAWallet, smartWalletIcon, } = useAccountWallet();
14
17
  const { data: walletImage } = useWalletImage(connectedEOAWallet?.id);
15
18
  const isMobile = useIsMobile();
@@ -19,6 +22,10 @@ export function SignIn(props) {
19
22
  };
20
23
  const connectedWallets = useConnectedWallets();
21
24
  const setActiveWallet = useSetActiveWallet();
25
+ // Better Auth state
26
+ const isAuthenticated = useAuthStore(state => state.isAuthenticated);
27
+ const { user } = useUser();
28
+ const userDisplayName = user?.username || user?.email || "Account";
22
29
  const handleSetActiveAccount = (selectedWalletId) => {
23
30
  if (!selectedWalletId ||
24
31
  !connectedWallets ||
@@ -34,12 +41,15 @@ export function SignIn(props) {
34
41
  setActiveWallet(connectedEOAWallet);
35
42
  }
36
43
  }, [connectedEOAWallet, isActiveEOAWallet, setActiveWallet, automaticallySetFirstEoa]);
44
+ const isLoggedIn = isBetterAuth ? isAuthenticated : !!globalAddress;
37
45
  // Desktop version - original dropdown menu
38
- return (_jsx(StyleRoot, { children: _jsx(Menu, { className: `relative flex items-center ${className || ""}`, as: "div", children: globalAddress ? (_jsxs(_Fragment, { children: [_jsxs(MenuButton, { className: "bg-b3-react-background group flex h-10 items-center gap-1 rounded-xl px-3 focus:outline-none", children: [!!walletImage && (_jsx(IPFSMediaRenderer, { src: walletImage, alt: "Wallet Image", className: "bg-b3-react-primary h-6 w-6 rounded-full object-cover opacity-100" })), _jsx("div", { className: "text-as-primary", children: ensName ? ensName : truncateAddress(globalAddress) })] }), _jsx(Transition, { enter: "duration-200 ease-out", enterFrom: "scale-95 opacity-0", enterTo: "scale-100 opacity-100", leave: "duration-300 ease-out", leaveFrom: "scale-100 opacity-100", leaveTo: "scale-95 opacity-0", children: _jsx(MenuItems, { className: "b3-root absolute -right-4 top-full min-w-64 rounded-2xl border focus:outline-none lg:right-0", modal: false,
46
+ return (_jsx(StyleRoot, { children: _jsx(Menu, { className: `relative flex items-center ${className || ""}`, as: "div", children: isLoggedIn ? (_jsxs(_Fragment, { children: [_jsxs(MenuButton, { className: "bg-b3-react-background group flex h-10 items-center gap-1 rounded-xl px-3 focus:outline-none", children: [isBetterAuth ? (_jsx(Avatar, { name: userDisplayName, variant: "beam", size: 24, colors: AVATAR_COLORS })) : (!!walletImage && (_jsx(IPFSMediaRenderer, { src: walletImage, alt: "Wallet Image", className: "bg-b3-react-primary h-6 w-6 rounded-full object-cover opacity-100" }))), _jsx("div", { className: "text-as-primary", children: isBetterAuth ? userDisplayName : ensName ? ensName : truncateAddress(globalAddress ?? "") })] }), _jsx(Transition, { enter: "duration-200 ease-out", enterFrom: "scale-95 opacity-0", enterTo: "scale-100 opacity-100", leave: "duration-300 ease-out", leaveFrom: "scale-100 opacity-100", leaveTo: "scale-95 opacity-0", children: _jsx(MenuItems, { className: "b3-root absolute -right-4 top-full min-w-64 rounded-2xl border focus:outline-none lg:right-0", modal: false,
39
47
  // TODO: Figure out why setting anchor on mobile causes z-index issues where it appears under elements
40
- anchor: isMobile ? "top end" : undefined, children: _jsxs("div", { className: "bg-b3-react-background", children: [connectedEOAWallet ? (_jsxs("div", { className: cn("border-b3-react-subtle bg-b3-react-background flex cursor-pointer items-center justify-between rounded-xl p-3"), onClick: () => handleSetActiveAccount(connectedEOAWallet?.id), children: [_jsxs("div", { className: "flex items-center", children: [_jsx("img", { className: "bg-b3-react-primary h-16 w-16 rounded-full opacity-100", src: walletImage, alt: connectedEOAWallet?.id }), _jsxs("div", { className: "ml-4 grow", children: [ensName && _jsx("div", { children: ensName }), _jsx("div", { children: truncateAddress(globalAddress) })] })] }), isActiveEOAWallet && _jsx(Icon, { className: "fill-b3-react-primary", name: "check" })] })) : (connectedSmartWallet && (_jsxs("div", { className: cn("mb-2 flex cursor-pointer items-center justify-between rounded-xl p-3", isActiveSmartWallet
48
+ anchor: isMobile ? "top end" : undefined, children: _jsxs("div", { className: "bg-b3-react-background", children: [isBetterAuth ? (
49
+ /* Better Auth: show user info instead of wallet switching */
50
+ _jsxs("div", { className: "flex items-center gap-3 rounded-xl p-3", children: [_jsx(Avatar, { name: userDisplayName, variant: "beam", size: 48, colors: AVATAR_COLORS }), _jsxs("div", { className: "flex flex-col gap-0.5", children: [user?.username && _jsx("div", { className: "text-b3-react-primary font-semibold", children: user.username }), user?.email && _jsx("div", { className: "text-b3-react-secondary text-sm", children: user.email })] })] })) : connectedEOAWallet ? (_jsxs("div", { className: cn("border-b3-react-subtle bg-b3-react-background flex cursor-pointer items-center justify-between rounded-xl p-3"), onClick: () => handleSetActiveAccount(connectedEOAWallet?.id), children: [_jsxs("div", { className: "flex items-center", children: [_jsx("img", { className: "bg-b3-react-primary h-16 w-16 rounded-full opacity-100", src: walletImage, alt: connectedEOAWallet?.id }), _jsxs("div", { className: "ml-4 grow", children: [ensName && _jsx("div", { children: ensName }), _jsx("div", { children: truncateAddress(globalAddress ?? "") })] })] }), isActiveEOAWallet && _jsx(Icon, { className: "fill-b3-react-primary", name: "check" })] })) : (connectedSmartWallet && (_jsxs("div", { className: cn("mb-2 flex cursor-pointer items-center justify-between rounded-xl p-3", isActiveSmartWallet
41
51
  ? "bg-b3-react-background"
42
- : "bg-b3-react-background hover:bg-b3-react-background"), onClick: () => handleSetActiveAccount(connectedSmartWallet?.id), children: [_jsxs("div", { className: "flex items-center", children: [_jsx("img", { className: "bg-b3-react-primary h-16 w-16 rounded-full opacity-100", src: smartWalletIcon, alt: connectedSmartWallet?.id }), _jsxs("div", { className: "grow pl-4", children: [ensName && _jsx("div", { children: ensName }), _jsx("div", { children: truncateAddress(globalAddress) }), _jsx("div", { children: "Smart wallet" })] })] }), isActiveSmartWallet && _jsx(Icon, { className: "fill-b3-react-primary", name: "check" })] }))), _jsx("div", { className: "ml-3", children: _jsx(ManageAccountButton, { ...props, className: "w-[calc(100%-12px)]" }) }), _jsx("button", { className: "mb-2 w-full space-y-1", onClick: onDisconnect, children: _jsxs("div", { className: "hover:bg-b3-react-background group flex h-12 items-center rounded-xl px-4 transition-colors", children: [_jsx(Icon, { className: "fill-b3-react-primary mr-4 shrink-0 transition-colors", name: "logout" }), _jsx("div", { className: "text-b3-react-primary mr-auto transition-colors", children: "Disconnect" })] }) })] }) }) })] })) : (_jsx(SignInWithB3, { closeAfterLogin: true, onLoginSuccess: async (globalAccount) => {
52
+ : "bg-b3-react-background hover:bg-b3-react-background"), onClick: () => handleSetActiveAccount(connectedSmartWallet?.id), children: [_jsxs("div", { className: "flex items-center", children: [_jsx("img", { className: "bg-b3-react-primary h-16 w-16 rounded-full opacity-100", src: smartWalletIcon, alt: connectedSmartWallet?.id }), _jsxs("div", { className: "grow pl-4", children: [ensName && _jsx("div", { children: ensName }), _jsx("div", { children: truncateAddress(globalAddress ?? "") }), _jsx("div", { children: "Smart wallet" })] })] }), isActiveSmartWallet && _jsx(Icon, { className: "fill-b3-react-primary", name: "check" })] }))), _jsx("div", { className: "ml-3", children: _jsx(ManageAccountButton, { ...props, className: "w-[calc(100%-12px)]" }) }), _jsx("button", { className: "mb-2 w-full space-y-1", onClick: onDisconnect, children: _jsxs("div", { className: "hover:bg-b3-react-background group flex h-12 items-center rounded-xl px-4 transition-colors", children: [_jsx(Icon, { className: "fill-b3-react-primary mr-4 shrink-0 transition-colors", name: "logout" }), _jsx("div", { className: "text-b3-react-primary mr-auto transition-colors", children: isBetterAuth ? "Sign out" : "Disconnect" })] }) })] }) }) })] })) : (_jsx(SignInWithB3, { closeAfterLogin: true, onLoginSuccess: async (globalAccount) => {
43
53
  console.log("User authenticated with Global Account!", globalAccount);
44
54
  }, ...props })) }) }));
45
55
  }
@@ -16,3 +16,4 @@ export declare const CLIENT_APP_BUNDLE_ID: string;
16
16
  export declare const B3_AUTH_COOKIE_NAME = "b3-auth";
17
17
  export declare const ENS_GATEWAY_URL = "https://ens-gateway.b3.fun/";
18
18
  export declare const PUBLIC_BASE_RPC_URL = "https://base-rpc.publicnode.com";
19
+ export declare const AVATAR_COLORS: string[];
@@ -21,3 +21,4 @@ export const CLIENT_APP_BUNDLE_ID = process.env.EXPO_PUBLIC_B3_BUNDLE_ID || "";
21
21
  export const B3_AUTH_COOKIE_NAME = "b3-auth";
22
22
  export const ENS_GATEWAY_URL = "https://ens-gateway.b3.fun/";
23
23
  export const PUBLIC_BASE_RPC_URL = "https://base-rpc.publicnode.com";
24
+ export const AVATAR_COLORS = ["#3368ef", "#272727", "#6366f1", "#06b6d4", "#eeb0d9", "#ba3fbf", "#ff777b", "#dfbb53"];
@@ -16,3 +16,4 @@ export declare const CLIENT_APP_BUNDLE_ID: string;
16
16
  export declare const B3_AUTH_COOKIE_NAME = "b3-auth";
17
17
  export declare const ENS_GATEWAY_URL = "https://ens-gateway.b3.fun/";
18
18
  export declare const PUBLIC_BASE_RPC_URL = "https://base-rpc.publicnode.com";
19
+ export declare const AVATAR_COLORS: string[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@b3dotfun/sdk",
3
- "version": "0.1.69-alpha.24",
3
+ "version": "0.1.69-alpha.25",
4
4
  "source": "src/index.ts",
5
5
  "main": "./dist/cjs/index.js",
6
6
  "react-native": "./dist/cjs/index.native.js",
@@ -62,6 +62,8 @@ export function AvatarEditor({ onSetAvatar, className }: AvatarEditorProps) {
62
62
  const setB3ModalContentType = useModalStore(state => state.setB3ModalContentType);
63
63
  const contentType = useModalStore(state => state.contentType);
64
64
 
65
+ const { authStrategy } = useB3Config();
66
+ const isBetterAuth = authStrategy === "better-auth";
65
67
  const account = useActiveAccount();
66
68
  const { data: profile, refetch: refreshProfile } = useProfile({
67
69
  address: account?.address,
@@ -162,7 +164,7 @@ export function AvatarEditor({ onSetAvatar, className }: AvatarEditorProps) {
162
164
  };
163
165
 
164
166
  const handleSaveChanges = async () => {
165
- if (!account?.address) {
167
+ if (!isBetterAuth && !account?.address) {
166
168
  toast.error("No account connected");
167
169
  return;
168
170
  }
@@ -1,4 +1,4 @@
1
- import { TabsListPrimitive, TabTriggerPrimitive, useModalStore } from "@b3dotfun/sdk/global-account/react";
1
+ import { TabsListPrimitive, TabTriggerPrimitive, useB3Config, useModalStore } from "@b3dotfun/sdk/global-account/react";
2
2
 
3
3
  const HomeIcon = () => {
4
4
  return (
@@ -42,6 +42,8 @@ const SettingsIcon = () => {
42
42
 
43
43
  const BottomNavigation = () => {
44
44
  const setB3ModalContentType = useModalStore(state => state.setB3ModalContentType);
45
+ const { authStrategy } = useB3Config();
46
+ const isBetterAuth = authStrategy === "better-auth";
45
47
 
46
48
  return (
47
49
  <div className="b3-modal-bottom-navigation sticky bottom-0 left-0 w-full rounded-b-xl border-t border-gray-200 bg-[#FAFAFA]">
@@ -54,19 +56,21 @@ const BottomNavigation = () => {
54
56
  <span className="text-b3-grey font-neue-montreal-semibold text-xs">Home</span>
55
57
  </TabTriggerPrimitive>
56
58
 
57
- <TabTriggerPrimitive
58
- value="swap"
59
- 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"
60
- onClick={() => {
61
- setB3ModalContentType({
62
- type: "anySpend",
63
- showBackButton: true,
64
- });
65
- }}
66
- >
67
- <SwapIcon />
68
- <span className="text-b3-grey font-neue-montreal-semibold text-xs">Swap</span>
69
- </TabTriggerPrimitive>
59
+ {!isBetterAuth && (
60
+ <TabTriggerPrimitive
61
+ value="swap"
62
+ 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"
63
+ onClick={() => {
64
+ setB3ModalContentType({
65
+ type: "anySpend",
66
+ showBackButton: true,
67
+ });
68
+ }}
69
+ >
70
+ <SwapIcon />
71
+ <span className="text-b3-grey font-neue-montreal-semibold text-xs">Swap</span>
72
+ </TabTriggerPrimitive>
73
+ )}
70
74
 
71
75
  <TabTriggerPrimitive
72
76
  value="settings"
@@ -2,10 +2,13 @@ import {
2
2
  CopyToClipboard,
3
3
  ManageAccountModalProps,
4
4
  useAuthentication,
5
+ useB3Config,
5
6
  useModalStore,
6
7
  } from "@b3dotfun/sdk/global-account/react";
8
+ import { AVATAR_COLORS } from "@b3dotfun/sdk/shared/constants";
7
9
  import * as AccordionPrimitive from "@radix-ui/react-accordion";
8
10
  import { AnimatePresence, motion } from "framer-motion";
11
+ import Avatar from "boring-avatars";
9
12
  import { Loader2 } from "lucide-react";
10
13
  import { useState } from "react";
11
14
  import { useActiveWallet, useConnectedWallets, useSetActiveWallet, useWalletImage } from "thirdweb/react";
@@ -89,7 +92,71 @@ function WalletItem({ wallet, isActive, onClick }: { wallet: Wallet; isActive: b
89
92
  );
90
93
  }
91
94
 
95
+ function BetterAuthHeader({ onLogout }: { onLogout?: () => void }) {
96
+ const contentType = useModalStore(state => state.contentType) as ManageAccountModalProps;
97
+ const setB3ModalOpen = useModalStore(state => state.setB3ModalOpen);
98
+ const partnerId = contentType?.partnerId;
99
+ const { logout, user } = useAuthentication(partnerId);
100
+ const [logoutLoading, setLogoutLoading] = useState(false);
101
+
102
+ const displayName = user?.username || user?.email || "Account";
103
+
104
+ const onLogoutEnhanced = async () => {
105
+ setLogoutLoading(true);
106
+ try {
107
+ await logout();
108
+ onLogout?.();
109
+ } finally {
110
+ setB3ModalOpen(false);
111
+ setLogoutLoading(false);
112
+ }
113
+ };
114
+
115
+ return (
116
+ <div className="bg-b3-background border-b3-line flex items-center justify-between border-b px-5 py-3">
117
+ <div className="flex items-center gap-2">
118
+ <div className="size-10 shrink-0 overflow-hidden rounded-full">
119
+ <Avatar name={displayName} variant="beam" size={40} colors={AVATAR_COLORS} />
120
+ </div>
121
+ <div className="flex flex-col gap-0.5">
122
+ {user?.username && (
123
+ <p className="text-b3-grey font-neue-montreal-semibold text-left text-sm">{user.username}</p>
124
+ )}
125
+ {user?.email && (
126
+ <div className="flex items-center gap-1">
127
+ <p className="text-b3-foreground-muted font-neue-montreal-medium text-sm">{user.email}</p>
128
+ <CopyToClipboard text={user.email} />
129
+ </div>
130
+ )}
131
+ </div>
132
+ </div>
133
+ <button
134
+ className="border-b3-line hover:bg-b3-line flex items-center justify-center gap-1.5 rounded-xl border border-solid px-3 py-2 transition-colors"
135
+ onClick={onLogoutEnhanced}
136
+ disabled={logoutLoading}
137
+ >
138
+ {logoutLoading ? (
139
+ <Loader2 className="animate-spin" size={16} />
140
+ ) : (
141
+ <SignOutIcon size={16} className="text-b3-grey" />
142
+ )}
143
+ <p className="text-b3-grey font-neue-montreal-semibold text-sm">Sign out</p>
144
+ </button>
145
+ </div>
146
+ );
147
+ }
148
+
92
149
  export function Header({ onLogout }: { onLogout?: () => void }) {
150
+ const { authStrategy } = useB3Config();
151
+
152
+ if (authStrategy === "better-auth") {
153
+ return <BetterAuthHeader onLogout={onLogout} />;
154
+ }
155
+
156
+ return <WalletHeader onLogout={onLogout} />;
157
+ }
158
+
159
+ function WalletHeader({ onLogout }: { onLogout?: () => void }) {
93
160
  const activeWallet = useActiveWallet();
94
161
 
95
162
  const connectedWallets = useConnectedWallets();
@@ -110,10 +177,13 @@ export function Header({ onLogout }: { onLogout?: () => void }) {
110
177
 
111
178
  const onLogoutEnhanced = async () => {
112
179
  setLogoutLoading(true);
113
- await logout();
114
- onLogout?.();
115
- setB3ModalOpen(false);
116
- setLogoutLoading(false);
180
+ try {
181
+ await logout();
182
+ onLogout?.();
183
+ } finally {
184
+ setB3ModalOpen(false);
185
+ setLogoutLoading(false);
186
+ }
117
187
  };
118
188
 
119
189
  const handleWalletSwitch = (wallet: Wallet) => {
@@ -1,3 +1,4 @@
1
+ import { useB3Config } from "@b3dotfun/sdk/global-account/react";
1
2
  import { Tabs, TabsContent, TabsList, TabTrigger } from "../ui/Tabs";
2
3
  import { Header } from "./Header";
3
4
  import HomeActions from "./HomeActions";
@@ -11,31 +12,36 @@ interface HomeContentProps {
11
12
  }
12
13
 
13
14
  export function HomeContent({ showDeposit = false, showSwap = true }: HomeContentProps) {
15
+ const { authStrategy } = useB3Config();
16
+ const isBetterAuth = authStrategy === "better-auth";
17
+
14
18
  return (
15
19
  <div className="flex flex-col">
16
20
  <Header />
17
21
  <div className="flex flex-col">
18
22
  <ProfileSection />
19
23
 
20
- <HomeActions showDeposit={showDeposit} showSwap={showSwap} />
21
- <div className="b3-modal-balance-content space-y-2 p-5">
22
- <Tabs defaultValue={"balance"}>
23
- <TabsList className="b3-modal-balance-tabs-list">
24
- <TabTrigger value="balance" className="font-neue-montreal-semibold p-0 pr-3">
25
- Balance
26
- </TabTrigger>
27
- <TabTrigger value="nfts" className="font-neue-montreal-semibold p-0 pr-3">
28
- NFTs
29
- </TabTrigger>
30
- </TabsList>
31
- <TabsContent value="balance" className="px-0 pb-4 pt-2">
32
- <TokenContent />
33
- </TabsContent>
34
- <TabsContent value="nfts" className="px-0 pb-4 pt-2">
35
- <NFTContent />
36
- </TabsContent>
37
- </Tabs>
38
- </div>
24
+ {!isBetterAuth && <HomeActions showDeposit={showDeposit} showSwap={showSwap} />}
25
+ {!isBetterAuth && (
26
+ <div className="b3-modal-balance-content space-y-2 p-5">
27
+ <Tabs defaultValue={"balance"}>
28
+ <TabsList className="b3-modal-balance-tabs-list">
29
+ <TabTrigger value="balance" className="font-neue-montreal-semibold p-0 pr-3">
30
+ Balance
31
+ </TabTrigger>
32
+ <TabTrigger value="nfts" className="font-neue-montreal-semibold p-0 pr-3">
33
+ NFTs
34
+ </TabTrigger>
35
+ </TabsList>
36
+ <TabsContent value="balance" className="px-0 pb-4 pt-2">
37
+ <TokenContent />
38
+ </TabsContent>
39
+ <TabsContent value="nfts" className="px-0 pb-4 pt-2">
40
+ <NFTContent />
41
+ </TabsContent>
42
+ </Tabs>
43
+ </div>
44
+ )}
39
45
  </div>
40
46
  </div>
41
47
  );
@@ -1,11 +1,13 @@
1
1
  import {
2
2
  useAccountWallet,
3
+ useB3Config,
3
4
  useModalStore,
4
5
  useProfile,
5
6
  useSimBalance,
6
7
  useUser,
7
8
  } from "@b3dotfun/sdk/global-account/react";
8
9
  import { validateImageUrl } from "@b3dotfun/sdk/global-account/react/utils/profileDisplay";
10
+ import { AVATAR_COLORS } from "@b3dotfun/sdk/shared/constants";
9
11
  import { formatUsername } from "@b3dotfun/sdk/shared/utils";
10
12
  import { formatDisplayNumber } from "@b3dotfun/sdk/shared/utils/number";
11
13
  import Avatar from "boring-avatars";
@@ -15,9 +17,9 @@ import { useActiveAccount } from "thirdweb/react";
15
17
  import { useFirstEOA } from "../../hooks/useFirstEOA";
16
18
  import { IPFSMediaRenderer } from "../IPFSMediaRenderer/IPFSMediaRenderer";
17
19
 
18
- const AVATAR_COLORS = ["#3368ef", "#272727", "#6366f1", "#06b6d4", "#eeb0d9", "#ba3fbf", "#ff777b", "#dfbb53"];
19
-
20
20
  const ProfileSection = () => {
21
+ const { authStrategy } = useB3Config();
22
+ const isBetterAuth = authStrategy === "better-auth";
21
23
  const account = useActiveAccount();
22
24
  const { address: eoaAddress } = useFirstEOA();
23
25
  const { address: smartWalletAddress } = useAccountWallet();
@@ -60,7 +62,7 @@ const ProfileSection = () => {
60
62
  }, [avatarSrc]);
61
63
 
62
64
  const currentUsername = user?.username || profile?.displayName || formatUsername(profile?.name || "");
63
- const avatarSeed = eoaAddress || account?.address || smartWalletAddress || currentUsername || "user";
65
+ const avatarSeed = eoaAddress || account?.address || smartWalletAddress || currentUsername || user?.email || "user";
64
66
 
65
67
  return (
66
68
  <div className="flex items-center justify-between px-5 py-6">
@@ -87,13 +89,18 @@ const ProfileSection = () => {
87
89
  </button>
88
90
  </div>
89
91
  <div className="global-account-profile-info flex flex-col gap-1">
90
- <h2 className="text-b3-grey font-neue-montreal-semibold flex h-[38px] items-center gap-1 text-xl">
91
- <div className="text-b3-foreground-muted"> $</div>
92
- <div className="text-[30px]">{formatDisplayNumber(totalBalanceUsd, { fractionDigits: 2 })}</div>
93
- </h2>
92
+ {!isBetterAuth && (
93
+ <h2 className="text-b3-grey font-neue-montreal-semibold flex h-[38px] items-center gap-1 text-xl">
94
+ <div className="text-b3-foreground-muted"> $</div>
95
+ <div className="text-[30px]">{formatDisplayNumber(totalBalanceUsd, { fractionDigits: 2 })}</div>
96
+ </h2>
97
+ )}
94
98
  <div className="b3-modal-username font-neue-montreal-semibold text-base leading-none text-[#0B57C2]">
95
99
  {currentUsername}
96
100
  </div>
101
+ {isBetterAuth && user?.email && (
102
+ <div className="text-b3-foreground-muted font-neue-montreal-medium text-sm">{user.email}</div>
103
+ )}
97
104
  </div>
98
105
  </div>
99
106
  </div>
@@ -1,4 +1,4 @@
1
- import { useAuthentication, useModalStore } from "@b3dotfun/sdk/global-account/react";
1
+ import { useAuthentication, useB3Config, useModalStore } from "@b3dotfun/sdk/global-account/react";
2
2
  import { client } from "@b3dotfun/sdk/shared/utils/thirdweb";
3
3
  import { getSessionDurationDays, SESSION_DURATION_LABELS } from "@b3dotfun/sdk/shared/utils/session-duration";
4
4
  import { Loader2 } from "lucide-react";
@@ -19,6 +19,8 @@ const SettingsContent = ({
19
19
  onLogout?: () => void;
20
20
  chain: Chain;
21
21
  }) => {
22
+ const { authStrategy } = useB3Config();
23
+ const isBetterAuth = authStrategy === "better-auth";
22
24
  const setB3ModalContentType = useModalStore(state => state.setB3ModalContentType);
23
25
  const setB3ModalOpen = useModalStore(state => state.setB3ModalOpen);
24
26
  const { logout, user } = useAuthentication(partnerId);
@@ -69,19 +71,21 @@ const SettingsContent = ({
69
71
 
70
72
  {/* Menu Items */}
71
73
  <div className="space-y-3 px-5">
72
- <SettingsMenuItem
73
- icon={
74
- <svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
75
- <path
76
- d="M0 12C0 5.37258 5.37258 0 12 0H28C34.6274 0 40 5.37258 40 12V28C40 34.6274 34.6274 40 28 40H12C5.37258 40 0 34.6274 0 28V12Z"
77
- fill="#F4F4F5"
78
- />
79
- </svg>
80
- }
81
- title="Linked Accounts"
82
- subtitle={`${profiles.length} connected account${profiles.length > 1 ? "s" : ""}`}
83
- onClick={() => handleNavigate("linkAccount")}
84
- />
74
+ {!isBetterAuth && (
75
+ <SettingsMenuItem
76
+ icon={
77
+ <svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
78
+ <path
79
+ d="M0 12C0 5.37258 5.37258 0 12 0H28C34.6274 0 40 5.37258 40 12V28C40 34.6274 34.6274 40 28 40H12C5.37258 40 0 34.6274 0 28V12Z"
80
+ fill="#F4F4F5"
81
+ />
82
+ </svg>
83
+ }
84
+ title="Linked Accounts"
85
+ subtitle={`${profiles.length} connected account${profiles.length > 1 ? "s" : ""}`}
86
+ onClick={() => handleNavigate("linkAccount")}
87
+ />
88
+ )}
85
89
  <SettingsMenuItem
86
90
  icon={
87
91
  <svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
@@ -18,7 +18,8 @@ const SettingsProfileCard = () => {
18
18
  address: eoaAddress || account?.address,
19
19
  fresh: true,
20
20
  });
21
- const { partnerId } = useB3Config();
21
+ const { partnerId, authStrategy } = useB3Config();
22
+ const isBetterAuth = authStrategy === "better-auth";
22
23
  const { user, setUser } = useAuthentication(partnerId);
23
24
  const setB3ModalOpen = useModalStore(state => state.setB3ModalOpen);
24
25
  const setB3ModalContentType = useModalStore(state => state.setB3ModalContentType);
@@ -78,25 +79,33 @@ const SettingsProfileCard = () => {
78
79
 
79
80
  setIsSaving(true);
80
81
  try {
81
- const sanitizedUsername = ens_normalize(editedUsername.trim());
82
- const b3Username = `${sanitizedUsername}.b3.fun`;
83
- const usernameSignMessage = `Register "${b3Username}"`;
84
- const usernameSignature = await account?.signMessage({ message: usernameSignMessage });
85
-
86
- if (!usernameSignature) {
87
- throw new Error("Failed to sign username registration message");
82
+ let updatedUser: Users;
83
+ if (isBetterAuth) {
84
+ // Better Auth: register username without wallet signing (DB-only, no ENS)
85
+ // Skip ens_normalize it rejects underscores/mixed-case that are valid non-ENS usernames
86
+ const sanitizedUsername = editedUsername.trim().toLowerCase();
87
+ // Type assertion needed: b3-mono now accepts message/hash as optional for Better Auth users
88
+ updatedUser = (await app
89
+ .service("users")
90
+ .registerUsername({ username: sanitizedUsername } as any, {} as any)) as unknown as Users;
91
+ } else {
92
+ // Thirdweb: register username with wallet signature + on-chain ENS
93
+ const sanitizedUsername = ens_normalize(editedUsername.trim());
94
+ const b3Username = `${sanitizedUsername}.b3.fun`;
95
+ const usernameSignMessage = `Register "${b3Username}"`;
96
+ const usernameSignature = await account?.signMessage({ message: usernameSignMessage });
97
+
98
+ if (!usernameSignature) {
99
+ throw new Error("Failed to sign username registration message");
100
+ }
101
+
102
+ updatedUser = (await app
103
+ .service("users")
104
+ .registerUsername(
105
+ { username: sanitizedUsername, message: usernameSignMessage, hash: usernameSignature },
106
+ {} as any,
107
+ )) as unknown as Users;
88
108
  }
89
-
90
- console.log("@@usernameSignature", usernameSignature);
91
-
92
- // Register username with ENS
93
- // Note: Type assertion needed until @b3dotfun/b3-api package is updated with RegisterUsername type
94
- const updatedUser = (await app
95
- .service("users")
96
- .registerUsername(
97
- { username: sanitizedUsername, message: usernameSignMessage, hash: usernameSignature },
98
- {} as any,
99
- )) as unknown as Users;
100
109
  // Update user state - registerUsername returns an array with single user
101
110
  setUser(Array.isArray(updatedUser) ? updatedUser[0] : updatedUser);
102
111
 
@@ -179,7 +188,7 @@ const SettingsProfileCard = () => {
179
188
  <>
180
189
  <div className="flex items-center gap-1">
181
190
  <p className="b3-modal-username font-neue-montreal-semibold text-lg leading-none text-[#0B57C2]">
182
- {currentUsername}
191
+ {currentUsername || user?.email}
183
192
  </p>
184
193
  </div>
185
194
  <button