@b3dotfun/sdk 0.0.30-alpha.4 → 0.0.30-alpha.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/dist/cjs/anyspend/react/components/common/ChainTokenIcon.d.ts +1 -1
  2. package/dist/cjs/anyspend/react/components/common/ChainTokenIcon.js +2 -1
  3. package/dist/cjs/global-account/react/components/ManageAccount/ContentTokens.d.ts +14 -0
  4. package/dist/cjs/global-account/react/components/ManageAccount/ContentTokens.js +246 -0
  5. package/dist/cjs/global-account/react/components/ManageAccount/ManageAccount.js +2 -5
  6. package/dist/cjs/global-account/react/hooks/index.d.ts +1 -0
  7. package/dist/cjs/global-account/react/hooks/index.js +3 -1
  8. package/dist/cjs/global-account/react/hooks/useSimBalance.d.ts +24 -0
  9. package/dist/cjs/global-account/react/hooks/useSimBalance.js +25 -0
  10. package/dist/cjs/global-account/react/hooks/useUnifiedChainSwitchAndExecute.js +0 -1
  11. package/dist/esm/anyspend/react/components/common/ChainTokenIcon.d.ts +1 -1
  12. package/dist/esm/anyspend/react/components/common/ChainTokenIcon.js +2 -1
  13. package/dist/esm/global-account/react/components/ManageAccount/ContentTokens.d.ts +14 -0
  14. package/dist/esm/global-account/react/components/ManageAccount/ContentTokens.js +243 -0
  15. package/dist/esm/global-account/react/components/ManageAccount/ManageAccount.js +2 -5
  16. package/dist/esm/global-account/react/hooks/index.d.ts +1 -0
  17. package/dist/esm/global-account/react/hooks/index.js +1 -0
  18. package/dist/esm/global-account/react/hooks/useSimBalance.d.ts +24 -0
  19. package/dist/esm/global-account/react/hooks/useSimBalance.js +22 -0
  20. package/dist/esm/global-account/react/hooks/useUnifiedChainSwitchAndExecute.js +0 -1
  21. package/dist/styles/index.css +1 -1
  22. package/dist/types/anyspend/react/components/common/ChainTokenIcon.d.ts +1 -1
  23. package/dist/types/global-account/react/components/ManageAccount/ContentTokens.d.ts +14 -0
  24. package/dist/types/global-account/react/hooks/index.d.ts +1 -0
  25. package/dist/types/global-account/react/hooks/useSimBalance.d.ts +24 -0
  26. package/package.json +1 -1
  27. package/src/anyspend/react/components/common/ChainTokenIcon.tsx +8 -2
  28. package/src/global-account/react/components/ManageAccount/ContentTokens.tsx +539 -0
  29. package/src/global-account/react/components/ManageAccount/ManageAccount.tsx +10 -64
  30. package/src/global-account/react/hooks/index.ts +1 -0
  31. package/src/global-account/react/hooks/useAccountAssets.ts +1 -0
  32. package/src/global-account/react/hooks/useSimBalance.ts +53 -0
  33. package/src/global-account/react/hooks/useUnifiedChainSwitchAndExecute.ts +0 -1
@@ -1,5 +1,5 @@
1
1
  export declare function ChainTokenIcon({ chainUrl, tokenUrl, className, }: {
2
2
  chainUrl: string;
3
- tokenUrl: string;
3
+ tokenUrl?: string;
4
4
  className?: string;
5
5
  }): import("react/jsx-runtime").JSX.Element;
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ChainTokenIcon = ChainTokenIcon;
4
4
  const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const lucide_react_1 = require("lucide-react");
5
6
  function ChainTokenIcon({ chainUrl, tokenUrl, className = "", }) {
6
- return ((0, jsx_runtime_1.jsxs)("div", { className: `relative ${className}`, children: [(0, jsx_runtime_1.jsx)("img", { src: tokenUrl, alt: "Token", className: "h-full w-full rounded-full" }), (0, jsx_runtime_1.jsx)("div", { className: "bg-as-on-surface-1 border-as-stroke absolute bottom-0 right-0 h-[45%] w-[45%] rounded border", children: (0, jsx_runtime_1.jsx)("img", { src: chainUrl, alt: "Chain", className: "h-full w-full rounded" }) })] }));
7
+ return ((0, jsx_runtime_1.jsxs)("div", { className: `relative ${className}`, children: [tokenUrl ? ((0, jsx_runtime_1.jsx)("img", { src: tokenUrl, alt: "Token", className: "h-full w-full rounded-full" })) : ((0, jsx_runtime_1.jsx)(lucide_react_1.HelpCircle, { className: "text-b3-react-foreground h-full w-full" })), (0, jsx_runtime_1.jsx)("div", { className: "bg-as-on-surface-1 border-as-stroke absolute bottom-0 right-0 h-[45%] w-[45%] rounded border", children: (0, jsx_runtime_1.jsx)("img", { src: chainUrl, alt: "Chain", className: "h-full w-full rounded" }) })] }));
7
8
  }
@@ -0,0 +1,14 @@
1
+ interface ContentTokensProps {
2
+ activeTab: string;
3
+ }
4
+ /**
5
+ * ContentTokens Component
6
+ *
7
+ * Displays user's token balances with ability to send tokens. Features:
8
+ * - Animated transitions between token list and send form
9
+ * - Smart filtering (shows all tokens when ≤5 valuable tokens or no $1+ tokens)
10
+ * - NumericFormat inputs for proper number handling
11
+ * - Focus preservation during transitions (see render functions pattern below)
12
+ */
13
+ export declare function ContentTokens({ activeTab }: ContentTokensProps): import("react/jsx-runtime").JSX.Element;
14
+ export {};
@@ -0,0 +1,246 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ContentTokens = ContentTokens;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const anyspend_1 = require("../../../../anyspend");
6
+ const ChainTokenIcon_1 = require("../../../../anyspend/react/components/common/ChainTokenIcon");
7
+ const react_1 = require("../../../../global-account/react");
8
+ const number_1 = require("../../../../shared/utils/number");
9
+ const lucide_react_1 = require("lucide-react");
10
+ const react_2 = require("react");
11
+ const react_number_format_1 = require("react-number-format");
12
+ const sonner_1 = require("sonner");
13
+ const react_3 = require("thirdweb/react");
14
+ const viem_1 = require("viem");
15
+ // Panel view enum for managing navigation between token list and send form
16
+ var TokenPanelView;
17
+ (function (TokenPanelView) {
18
+ TokenPanelView[TokenPanelView["LIST"] = 0] = "LIST";
19
+ TokenPanelView[TokenPanelView["SEND"] = 1] = "SEND";
20
+ })(TokenPanelView || (TokenPanelView = {}));
21
+ /**
22
+ * ContentTokens Component
23
+ *
24
+ * Displays user's token balances with ability to send tokens. Features:
25
+ * - Animated transitions between token list and send form
26
+ * - Smart filtering (shows all tokens when ≤5 valuable tokens or no $1+ tokens)
27
+ * - NumericFormat inputs for proper number handling
28
+ * - Focus preservation during transitions (see render functions pattern below)
29
+ */
30
+ function ContentTokens({ activeTab }) {
31
+ // === TOKEN FILTERING STATE ===
32
+ const [showAllTokens, setShowAllTokens] = (0, react_2.useState)(false);
33
+ // === NAVIGATION STATE ===
34
+ const [tokenPanelView, setTokenPanelView] = (0, react_2.useState)(TokenPanelView.LIST);
35
+ // === SEND FORM STATE ===
36
+ const [selectedToken, setSelectedToken] = (0, react_2.useState)(null);
37
+ const [recipientAddress, setRecipientAddress] = (0, react_2.useState)("");
38
+ const [sendAmount, setSendAmount] = (0, react_2.useState)("");
39
+ const [isSending, setIsSending] = (0, react_2.useState)(false);
40
+ const [addressError, setAddressError] = (0, react_2.useState)("");
41
+ // === ANIMATION STATE ===
42
+ // CRITICAL: useRef for animation direction prevents component remounting
43
+ // This ensures input focus is preserved during panel transitions
44
+ const animationDirection = (0, react_2.useRef)(null);
45
+ // === DATA FETCHING ===
46
+ const account = (0, react_3.useActiveAccount)();
47
+ const { data: simBalance, refetch: refetchSimBalance, isLoading: isLoadingBalance } = (0, react_1.useSimBalance)(account?.address);
48
+ // === BLOCKCHAIN INTERACTION ===
49
+ const { switchChainAndExecute } = (0, react_1.useUnifiedChainSwitchAndExecute)();
50
+ // === ADDRESS VALIDATION ===
51
+ // Handle recipient address change with real-time validation using viem
52
+ const handleRecipientAddressChange = (value) => {
53
+ setRecipientAddress(value);
54
+ // Only show error if user has typed something and it's invalid
55
+ // Using viem's isAddress for robust EVM address validation
56
+ if (value && !(0, viem_1.isAddress)(value)) {
57
+ setAddressError("Please enter a valid EVM address (0x...)");
58
+ }
59
+ else {
60
+ setAddressError("");
61
+ }
62
+ };
63
+ // === TAB RESET EFFECT ===
64
+ // Reset all state when user switches away from tokens tab
65
+ (0, react_2.useEffect)(() => {
66
+ if (activeTab !== "tokens") {
67
+ setTokenPanelView(TokenPanelView.LIST);
68
+ setSelectedToken(null);
69
+ setRecipientAddress("");
70
+ setSendAmount("");
71
+ setIsSending(false);
72
+ setAddressError("");
73
+ animationDirection.current = null;
74
+ }
75
+ }, [activeTab]);
76
+ // === HELPER FUNCTION ===
77
+ // Get current version of selected token from fresh balance data
78
+ // 🔧 FIX: Prevents auto-navigation back to token list when balance refreshes
79
+ // The useSimBalance hook refreshes data, creating new token object references
80
+ // This helper ensures we always get the fresh token data instead of stale references
81
+ const getCurrentSelectedToken = () => {
82
+ if (!selectedToken || !simBalance?.balances) {
83
+ return null;
84
+ }
85
+ const found = simBalance.balances.find(token => token.chain_id === selectedToken.chain_id && token.address === selectedToken.address);
86
+ return found || null;
87
+ };
88
+ // ==================================================================================
89
+ // === RENDER FUNCTIONS (NOT COMPONENTS!) ===
90
+ // ==================================================================================
91
+ //
92
+ // 🚨 CRITICAL ARCHITECTURE DECISION:
93
+ // These are render functions, NOT component functions with useCallback!
94
+ //
95
+ // WHY THIS WORKS:
96
+ // ✅ Stable wrapper <div> elements with consistent keys
97
+ // ✅ React never sees these as "new components"
98
+ // ✅ Input focus is preserved during transitions
99
+ // ✅ No component remounting issues
100
+ //
101
+ // WHY useCallback DIDN'T WORK:
102
+ // ❌ useCallback(() => <JSX />, [deps]) still creates new component instances
103
+ // ❌ React treats each render as a different component
104
+ // ❌ Causes remounting and focus loss
105
+ //
106
+ // THE PATTERN:
107
+ // Instead of: {[<ComponentA />, <ComponentB />]}
108
+ // We use: {[<div key="a">{renderA()}</div>, <div key="b">{renderB()}</div>]}
109
+ // ==================================================================================
110
+ /**
111
+ * Renders the send token form panel
112
+ * Includes recipient input, amount input with NumericFormat, and percentage buttons
113
+ */
114
+ const renderSendTokenPanel = () => {
115
+ // Get fresh token data to prevent stale references
116
+ const currentToken = getCurrentSelectedToken();
117
+ // 🔧 SINGLE FALLBACK STRATEGY:
118
+ // Use fresh token data when available, fall back to selectedToken if needed
119
+ // This prevents duplication of "currentToken || selectedToken" throughout the component
120
+ const displayToken = currentToken || selectedToken;
121
+ // Handle percentage button clicks (25%, 50%, 75%, 100%)
122
+ const handlePercentageClick = (percentage) => {
123
+ if (displayToken) {
124
+ const tokenBalance = (BigInt(displayToken.amount) * BigInt(percentage)) / BigInt(100);
125
+ const amount = (0, number_1.formatTokenAmount)(tokenBalance, displayToken.decimals, 30, false);
126
+ setSendAmount(amount);
127
+ }
128
+ };
129
+ // Execute token transfer transaction
130
+ const handleSend = async () => {
131
+ if (!displayToken || !recipientAddress || !sendAmount || parseFloat(sendAmount) <= 0) {
132
+ return;
133
+ }
134
+ setIsSending(true);
135
+ const amountInWei = (0, viem_1.parseUnits)(sendAmount, displayToken.decimals);
136
+ try {
137
+ const sendTokenData = (0, viem_1.encodeFunctionData)({
138
+ abi: viem_1.erc20Abi,
139
+ functionName: "transfer",
140
+ args: [recipientAddress, amountInWei],
141
+ });
142
+ const tx = await switchChainAndExecute(displayToken.chain_id, {
143
+ to: displayToken.address === "native" ? recipientAddress : displayToken.address,
144
+ data: sendTokenData,
145
+ value: displayToken.address === "native" ? amountInWei : BigInt(0),
146
+ });
147
+ if (tx) {
148
+ // Reset form
149
+ setSendAmount("");
150
+ }
151
+ }
152
+ catch (error) {
153
+ // Error
154
+ sonner_1.toast.error(`Failed to send ${displayToken.symbol}: ${error.message || "Unknown error"}`);
155
+ }
156
+ finally {
157
+ // Wait 1 second to make sure the tx is indexed on sim api.
158
+ setTimeout(async () => {
159
+ // Force refetch to bypass cache and get fresh balance data
160
+ await refetchSimBalance();
161
+ }, 1000);
162
+ setIsSending(false);
163
+ }
164
+ };
165
+ // Show loading state only if no token data is available at all
166
+ if (!displayToken) {
167
+ return ((0, jsx_runtime_1.jsxs)("div", { className: "flex flex-col items-center justify-center py-12 text-center", children: [(0, jsx_runtime_1.jsx)("div", { className: "bg-b3-line/50 mb-4 rounded-full p-4", children: (0, jsx_runtime_1.jsx)(lucide_react_1.Loader2, { className: "text-b3-foreground-muted h-8 w-8 animate-spin" }) }), (0, jsx_runtime_1.jsx)("h3", { className: "text-b3-grey font-neue-montreal-semibold mb-2", children: "Loading token data..." }), (0, jsx_runtime_1.jsx)("p", { className: "text-b3-foreground-muted font-neue-montreal-medium text-sm", children: "Please wait while we fetch the latest information" })] }));
168
+ }
169
+ return ((0, jsx_runtime_1.jsxs)("div", { className: "space-y-6", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-3", children: [(0, jsx_runtime_1.jsx)(react_1.Button, { variant: "ghost", size: "icon", onClick: () => {
170
+ animationDirection.current = "back";
171
+ setTokenPanelView(TokenPanelView.LIST);
172
+ }, className: "hover:bg-b3-line/60", disabled: isSending, children: (0, jsx_runtime_1.jsx)(lucide_react_1.ArrowLeft, { className: "h-5 w-5" }) }), (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-3", children: [(0, jsx_runtime_1.jsx)("div", { className: "flex h-8 w-8 items-center justify-center", children: anyspend_1.ALL_CHAINS[displayToken.chain_id]?.logoUrl ? ((0, jsx_runtime_1.jsx)(ChainTokenIcon_1.ChainTokenIcon, { chainUrl: anyspend_1.ALL_CHAINS[displayToken.chain_id].logoUrl, tokenUrl: displayToken.token_metadata?.logo, className: "size-8" })) : ((0, jsx_runtime_1.jsx)(lucide_react_1.CircleHelp, { className: "text-b3-react-foreground size-8" })) }), (0, jsx_runtime_1.jsxs)("h2", { className: "text-b3-grey font-neue-montreal-semibold text-lg", children: ["Send ", displayToken.symbol] })] })] }), (0, jsx_runtime_1.jsxs)("div", { className: "space-y-2", children: [(0, jsx_runtime_1.jsx)("label", { className: "text-b3-grey font-neue-montreal-medium text-sm", children: "Recipient Address" }), (0, jsx_runtime_1.jsxs)("div", { className: "space-y-1", children: [(0, jsx_runtime_1.jsxs)("div", { className: "relative", children: [(0, jsx_runtime_1.jsx)("input", { type: "text", value: recipientAddress, onChange: e => handleRecipientAddressChange(e.target.value), placeholder: "Enter wallet address (0x...)", className: `border-b3-line bg-b3-background text-b3-grey font-neue-montreal-medium placeholder:text-b3-foreground-muted w-full rounded-xl border px-4 py-3 pr-12 focus:outline-none disabled:cursor-not-allowed disabled:opacity-50 ${addressError ? "border-red-500 focus:border-red-500" : "focus:border-b3-primary-blue"}`, disabled: isSending }), (0, jsx_runtime_1.jsx)(react_1.Button, { variant: "ghost", size: "icon", className: "hover:bg-b3-line/60 absolute right-2 top-1/2 h-8 w-8 -translate-y-1/2", disabled: isSending, onClick: () => {
173
+ navigator.clipboard.readText().then(text => {
174
+ handleRecipientAddressChange(text);
175
+ });
176
+ }, children: (0, jsx_runtime_1.jsx)(lucide_react_1.Copy, { className: "h-4 w-4" }) })] }), addressError && (0, jsx_runtime_1.jsx)("p", { className: "font-neue-montreal-medium text-xs text-red-500", children: addressError })] })] }), (0, jsx_runtime_1.jsxs)("div", { className: "space-y-2", children: [(0, jsx_runtime_1.jsx)("label", { className: "text-b3-grey font-neue-montreal-medium text-sm", children: "Amount" }), (0, jsx_runtime_1.jsxs)("div", { className: "space-y-3", children: [(0, jsx_runtime_1.jsx)(react_number_format_1.NumericFormat, { decimalSeparator: ".", allowedDecimalSeparators: [","], thousandSeparator: true, inputMode: "decimal", autoComplete: "off", autoCorrect: "off", type: "text", placeholder: "0.00", minLength: 1, maxLength: 30, spellCheck: "false", className: "border-b3-line bg-b3-background text-b3-grey font-neue-montreal-medium placeholder:text-b3-foreground-muted focus:border-b3-primary-blue w-full rounded-xl border px-4 py-3 focus:outline-none disabled:cursor-not-allowed disabled:opacity-50", pattern: "^[0-9]*[.,]?[0-9]*$", disabled: isSending, value: sendAmount, allowNegative: false, onChange: e => setSendAmount(e.currentTarget.value) }), (0, jsx_runtime_1.jsx)("div", { className: "grid grid-cols-4 gap-2", children: [25, 50, 75, 100].map(percentage => ((0, jsx_runtime_1.jsxs)(react_1.Button, { variant: "outline", onClick: () => handlePercentageClick(percentage), className: "hover:bg-b3-primary-wash border-b3-line text-b3-grey font-neue-montreal-medium text-sm", disabled: isSending, children: [percentage, "%"] }, percentage))) }), (0, jsx_runtime_1.jsxs)("div", { className: "text-b3-foreground-muted font-neue-montreal-medium text-sm", children: ["Available: ", (0, number_1.formatTokenAmount)(BigInt(displayToken.amount), displayToken.decimals), " ", displayToken.symbol] })] })] }), (0, jsx_runtime_1.jsx)(react_1.Button, { onClick: handleSend, disabled: !recipientAddress ||
177
+ !sendAmount ||
178
+ parseFloat(sendAmount) <= 0 ||
179
+ isSending ||
180
+ !!addressError ||
181
+ !(0, viem_1.isAddress)(recipientAddress), className: "bg-b3-primary-blue hover:bg-b3-primary-blue/90 font-neue-montreal-semibold disabled:bg-b3-line disabled:text-b3-foreground-muted w-full rounded-xl py-3 text-white", children: isSending ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Loader2, { className: "mr-2 h-4 w-4 animate-spin" }), "Sending..."] })) : ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Send, { className: "mr-2 h-4 w-4" }), "Send ", displayToken.symbol] })) })] }));
182
+ };
183
+ // Skeleton loading component for token list
184
+ const LoadingIndicator = () => ((0, jsx_runtime_1.jsx)("div", { className: "space-y-4", children: (0, jsx_runtime_1.jsx)("div", { className: "space-y-1", children: [...Array(3)].map((_, index) => ((0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-between rounded-xl p-3", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-3", children: [(0, jsx_runtime_1.jsx)("div", { className: "bg-b3-line h-10 w-10 animate-pulse rounded-full" }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("div", { className: "bg-b3-line mb-1 h-4 w-16 animate-pulse rounded" }), (0, jsx_runtime_1.jsx)("div", { className: "bg-b3-line h-3 w-24 animate-pulse rounded" })] })] }), (0, jsx_runtime_1.jsxs)("div", { className: "text-right", children: [(0, jsx_runtime_1.jsx)("div", { className: "bg-b3-line mb-1 h-4 w-20 animate-pulse rounded" }), (0, jsx_runtime_1.jsx)("div", { className: "bg-b3-line h-3 w-16 animate-pulse rounded" })] })] }, index))) }) }));
185
+ /**
186
+ * Renders the token list panel with smart filtering
187
+ * Features intelligent token display logic to reduce noise while ensuring visibility
188
+ */
189
+ const renderTokenListPanel = () => {
190
+ // Show loading indicator when balance is loading
191
+ if (isLoadingBalance) {
192
+ return (0, jsx_runtime_1.jsx)(LoadingIndicator, {});
193
+ }
194
+ // Show empty state when no account or no balance data
195
+ if (!account?.address || !simBalance) {
196
+ return ((0, jsx_runtime_1.jsxs)("div", { className: "flex flex-col items-center justify-center py-12 text-center", children: [(0, jsx_runtime_1.jsx)("div", { className: "bg-b3-line/50 mb-4 rounded-full p-4", children: (0, jsx_runtime_1.jsx)(lucide_react_1.Loader2, { className: "text-b3-foreground-muted h-8 w-8" }) }), (0, jsx_runtime_1.jsx)("h3", { className: "text-b3-grey font-neue-montreal-semibold mb-2", children: "No wallet connected" }), (0, jsx_runtime_1.jsx)("p", { className: "text-b3-foreground-muted font-neue-montreal-medium text-sm", children: "Connect your wallet to view token balances" })] }));
197
+ }
198
+ // === SMART FILTERING LOGIC ===
199
+ // Filter tokens with value >= $1 to reduce noise from dust tokens
200
+ const filteredTokens = simBalance?.balances.filter(token => token.value_usd !== undefined && token.value_usd >= 1) || [];
201
+ // 🧠 INTELLIGENT DISPLAY LOGIC:
202
+ // Show all tokens automatically when filtering would be unhelpful:
203
+ // 1. User explicitly requested to show all, OR
204
+ // 2. No tokens with value >= $1 (user has only dust), OR
205
+ // 3. 5 or fewer tokens with value >= $1 (not enough to warrant filtering)
206
+ const shouldShowAllTokens = showAllTokens || filteredTokens.length === 0 || filteredTokens.length <= 5;
207
+ const tokensToShow = shouldShowAllTokens ? simBalance?.balances || [] : filteredTokens;
208
+ const hasHiddenTokens = !shouldShowAllTokens && (simBalance?.balances.length || 0) > filteredTokens.length;
209
+ // Handle token selection and navigate to send form
210
+ const handleTokenClick = (token) => {
211
+ setSelectedToken(token);
212
+ animationDirection.current = "forward"; // Set animation direction BEFORE state change
213
+ setTokenPanelView(TokenPanelView.SEND);
214
+ // Reset form when selecting a new token
215
+ setRecipientAddress("");
216
+ setSendAmount("");
217
+ setIsSending(false);
218
+ setAddressError("");
219
+ };
220
+ // Show empty state when no tokens are available
221
+ if (tokensToShow.length === 0) {
222
+ return ((0, jsx_runtime_1.jsxs)("div", { className: "flex flex-col items-center justify-center py-12 text-center", children: [(0, jsx_runtime_1.jsx)("div", { className: "bg-b3-line/50 mb-4 rounded-full p-4", children: (0, jsx_runtime_1.jsx)(lucide_react_1.CircleHelp, { className: "text-b3-foreground-muted h-8 w-8" }) }), (0, jsx_runtime_1.jsx)("h3", { className: "text-b3-grey font-neue-montreal-semibold mb-2", children: "No tokens found" }), (0, jsx_runtime_1.jsx)("p", { className: "text-b3-foreground-muted font-neue-montreal-medium text-sm", children: "No token balances found in your wallet." })] }));
223
+ }
224
+ return ((0, jsx_runtime_1.jsxs)("div", { className: "space-y-4", children: [(0, jsx_runtime_1.jsx)("div", { className: "space-y-1", children: tokensToShow.map(token => ((0, jsx_runtime_1.jsxs)("div", { className: "hover:bg-b3-line/60 dark:hover:bg-b3-primary-wash/40 group flex cursor-pointer items-center justify-between rounded-xl p-3 transition-all duration-200", onClick: () => handleTokenClick(token), children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-3", children: [(0, jsx_runtime_1.jsx)("div", { className: "flex h-10 w-10 items-center justify-center rounded-full", children: anyspend_1.ALL_CHAINS[token.chain_id]?.logoUrl ? ((0, jsx_runtime_1.jsx)(ChainTokenIcon_1.ChainTokenIcon, { chainUrl: anyspend_1.ALL_CHAINS[token.chain_id].logoUrl, tokenUrl: token.token_metadata?.logo, className: "size-10" })) : ((0, jsx_runtime_1.jsx)(lucide_react_1.CircleHelp, { className: "text-b3-react-foreground size-10" })) }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("div", { className: "flex items-center gap-2", children: (0, jsx_runtime_1.jsx)("span", { className: "text-b3-grey font-neue-montreal-semibold transition-colors duration-200 group-hover:font-bold group-hover:text-black", children: token.symbol }) }), (0, jsx_runtime_1.jsx)("div", { className: "text-b3-foreground-muted font-neue-montreal-medium text-sm transition-colors duration-200 group-hover:text-gray-700", children: token.name })] })] }), (0, jsx_runtime_1.jsxs)("div", { className: "text-right", children: [(0, jsx_runtime_1.jsx)("div", { className: "text-b3-grey font-neue-montreal-semibold transition-colors duration-200 group-hover:font-bold group-hover:text-black", children: (0, number_1.formatTokenAmount)(BigInt(token.amount), token.decimals) }), (0, jsx_runtime_1.jsx)("div", { className: "text-b3-foreground-muted font-neue-montreal-medium text-sm transition-colors duration-200 group-hover:text-gray-700", children: (0, number_1.formatDisplayNumber)(token.value_usd, { style: "currency", fractionDigits: 2 }) })] })] }, token.chain_id + "_" + token.address))) }), hasHiddenTokens && !showAllTokens && ((0, jsx_runtime_1.jsx)("div", { className: "flex justify-center", children: (0, jsx_runtime_1.jsx)(react_1.Button, { variant: "ghost", className: "text-b3-primary-blue hover:text-b3-primary-blue/80 font-neue-montreal-semibold text-sm", onClick: () => setShowAllTokens(true), children: "Show more" }) }))] }));
225
+ };
226
+ // === ANIMATION CONFIGURATION ===
227
+ // Memoize variants to prevent re-creation and unwanted re-renders
228
+ const variants = (0, react_2.useMemo)(() => ({
229
+ enter: (direction) => ({
230
+ x: direction === "back" ? -300 : 300, // Back: slide from left, Forward: slide from right
231
+ opacity: 0,
232
+ }),
233
+ center: { x: 0, opacity: 1 }, // Final position: centered and visible
234
+ exit: (direction) => ({
235
+ x: direction === "back" ? 300 : -300, // Back: slide to right, Forward: slide to left
236
+ opacity: 0,
237
+ }),
238
+ }), []);
239
+ // Memoize transition config for consistent spring animation
240
+ const transition = (0, react_2.useMemo)(() => ({
241
+ type: "spring",
242
+ stiffness: 300, // Spring tension
243
+ damping: 30, // Spring damping
244
+ }), []);
245
+ return ((0, jsx_runtime_1.jsx)(react_1.TransitionPanel, { activeIndex: tokenPanelView, className: "min-h-[400px]", custom: animationDirection.current, variants: variants, transition: transition, children: [(0, jsx_runtime_1.jsx)("div", { children: renderTokenListPanel() }, "token-list"), (0, jsx_runtime_1.jsx)("div", { children: renderSendTokenPanel() }, "send-token")] }));
246
+ }
@@ -19,6 +19,7 @@ const viem_1 = require("viem");
19
19
  const useFirstEOA_1 = __importDefault(require("../../hooks/useFirstEOA"));
20
20
  const profileDisplay_1 = require("../../utils/profileDisplay");
21
21
  const AccountAssets_1 = require("../AccountAssets/AccountAssets");
22
+ const ContentTokens_1 = require("./ContentTokens");
22
23
  function centerTruncate(str, length = 4) {
23
24
  if (str.length <= length * 2)
24
25
  return str;
@@ -45,8 +46,6 @@ function ManageAccount({ onLogout, onSwap: _onSwap, onDeposit: _onDeposit, chain
45
46
  const { activeTab = "overview", setActiveTab } = contentType;
46
47
  const { logout } = (0, react_1.useAuthentication)(partnerId);
47
48
  const [logoutLoading, setLogoutLoading] = (0, react_2.useState)(false);
48
- console.log("account", account);
49
- console.log("eoaAddress", eoaAddress);
50
49
  const { removeSessionKey } = (0, react_1.useRemoveSessionKey)({
51
50
  chain,
52
51
  onSuccess: tx => {
@@ -87,8 +86,6 @@ function ManageAccount({ onLogout, onSwap: _onSwap, onDeposit: _onDeposit, chain
87
86
  });
88
87
  }, children: [(0, jsx_runtime_1.jsx)(SwapIcon_1.SwapIcon, { size: 24, className: "text-b3-primary-blue" }), (0, jsx_runtime_1.jsx)("div", { className: "text-b3-grey font-neue-montreal-semibold", children: "Swap" })] })] }), (0, jsx_runtime_1.jsxs)("div", { className: "space-y-4", children: [(0, jsx_runtime_1.jsx)("h3", { className: "text-b3-grey font-neue-montreal-semibold", children: "Balance" }), (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-between", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-3", children: [(0, jsx_runtime_1.jsx)("div", { className: "flex h-10 w-10 items-center justify-center rounded-full", children: (0, jsx_runtime_1.jsx)("img", { src: "https://cdn.b3.fun/b3-coin-3d.png", alt: "B3", className: "size-10" }) }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("div", { className: "flex items-center gap-2", children: (0, jsx_runtime_1.jsx)("span", { className: "text-b3-grey font-neue-montreal-semibold", children: "B3" }) }), (0, jsx_runtime_1.jsxs)("div", { className: "text-b3-foreground-muted font-neue-montreal-medium text-sm", children: [b3Balance?.formattedTotal || "0.00", " B3"] })] })] }), (0, jsx_runtime_1.jsxs)("div", { className: "text-right", children: [(0, jsx_runtime_1.jsxs)("div", { className: "text-b3-grey font-neue-montreal-semibold", children: ["$", b3Balance?.balanceUsdFormatted || "0.00"] }), (0, jsx_runtime_1.jsx)("div", { className: "flex items-center gap-1", children: b3Balance?.priceChange24h !== null && b3Balance?.priceChange24h !== undefined ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Triangle, { className: `size-3 ${b3Balance.priceChange24h >= 0 ? "text-b3-positive fill-b3-positive" : "text-b3-negative fill-b3-negative rotate-180"}` }), (0, jsx_runtime_1.jsxs)("span", { className: `font-neue-montreal-medium text-sm ${b3Balance.priceChange24h >= 0 ? "text-b3-positive" : "text-b3-negative"}`, children: [b3Balance.priceChange24h >= 0 ? "+" : "", b3Balance.priceChange24h.toFixed(2), "%"] })] })) : ((0, jsx_runtime_1.jsx)("span", { className: "text-b3-foreground-muted font-neue-montreal-medium text-sm", 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-3", children: [(0, jsx_runtime_1.jsx)("div", { className: "flex h-10 w-10 items-center justify-center rounded-full", children: (0, jsx_runtime_1.jsx)("img", { src: "https://cdn.b3.fun/ethereum.svg", alt: "ETH", className: "size-10" }) }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("div", { className: "flex items-center gap-2", children: (0, jsx_runtime_1.jsx)("span", { className: "text-b3-grey font-neue-montreal-semibold", children: "Ethereum" }) }), (0, jsx_runtime_1.jsxs)("div", { className: "text-b3-foreground-muted font-neue-montreal-medium text-sm", children: [nativeBalance?.formattedTotal || "0.00", " ETH"] })] })] }), (0, jsx_runtime_1.jsxs)("div", { className: "text-right", children: [(0, jsx_runtime_1.jsxs)("div", { className: "text-b3-grey font-neue-montreal-semibold", children: ["$", nativeBalance?.formattedTotalUsd || "0.00"] }), (0, jsx_runtime_1.jsx)("div", { className: "flex items-center gap-2", children: nativeBalance?.priceChange24h !== null && nativeBalance?.priceChange24h !== undefined ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Triangle, { className: `size-3 ${nativeBalance.priceChange24h >= 0 ? "text-b3-positive fill-b3-positive" : "text-b3-negative fill-b3-negative rotate-180"}` }), (0, jsx_runtime_1.jsxs)("span", { className: `font-neue-montreal-medium text-sm ${nativeBalance.priceChange24h >= 0 ? "text-b3-positive" : "text-b3-negative"}`, children: [nativeBalance.priceChange24h >= 0 ? "+" : "", nativeBalance.priceChange24h.toFixed(2), "%"] })] })) : ((0, jsx_runtime_1.jsx)("span", { className: "text-b3-foreground-muted font-neue-montreal-medium text-sm", children: "--" })) })] })] })] }), eoaAddress && ((0, jsx_runtime_1.jsxs)("div", { className: "space-y-4", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-3", children: [(0, jsx_runtime_1.jsxs)("h3", { className: "text-b3-grey font-neue-montreal-semibold", children: ["Connected ", eoaInfo?.data?.name || "Wallet"] }), (0, jsx_runtime_1.jsxs)("div", { className: "border-b3-line bg-b3-line/20 hover:bg-b3-line/40 flex w-fit items-center gap-2 rounded-full border px-3 py-1 transition-colors", children: [(0, jsx_runtime_1.jsx)("span", { className: "text-b3-foreground-muted font-mono text-xs", children: centerTruncate(eoaAddress, 6) }), (0, jsx_runtime_1.jsx)(react_1.CopyToClipboard, { text: eoaAddress })] })] }), (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-between", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-3", children: [(0, jsx_runtime_1.jsx)("div", { className: "flex h-10 w-10 items-center justify-center rounded-full", children: (0, jsx_runtime_1.jsx)("img", { src: "https://cdn.b3.fun/b3-coin-3d.png", alt: "B3", className: "size-10" }) }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("div", { className: "flex items-center gap-2", children: (0, jsx_runtime_1.jsx)("span", { className: "text-b3-grey font-neue-montreal-semibold", children: "B3" }) }), (0, jsx_runtime_1.jsxs)("div", { className: "text-b3-foreground-muted font-neue-montreal-medium text-sm", children: [eoaB3Balance?.formattedTotal || "0.00", " B3"] })] })] }), (0, jsx_runtime_1.jsxs)("div", { className: "text-right", children: [(0, jsx_runtime_1.jsxs)("div", { className: "text-b3-grey font-neue-montreal-semibold", children: ["$", eoaB3Balance?.balanceUsdFormatted || "0.00"] }), (0, jsx_runtime_1.jsx)("div", { className: "flex items-center gap-1", children: eoaB3Balance?.priceChange24h !== null && eoaB3Balance?.priceChange24h !== undefined ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Triangle, { className: `size-3 ${eoaB3Balance.priceChange24h >= 0 ? "text-b3-positive fill-b3-positive" : "text-b3-negative fill-b3-negative rotate-180"}` }), (0, jsx_runtime_1.jsxs)("span", { className: `font-neue-montreal-medium text-sm ${eoaB3Balance.priceChange24h >= 0 ? "text-b3-positive" : "text-b3-negative"}`, children: [eoaB3Balance.priceChange24h >= 0 ? "+" : "", eoaB3Balance.priceChange24h.toFixed(2), "%"] })] })) : ((0, jsx_runtime_1.jsx)("span", { className: "text-b3-foreground-muted font-neue-montreal-medium text-sm", 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-3", children: [(0, jsx_runtime_1.jsx)("div", { className: "flex h-10 w-10 items-center justify-center rounded-full", children: (0, jsx_runtime_1.jsx)("img", { src: "https://cdn.b3.fun/ethereum.svg", alt: "ETH", className: "size-10" }) }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("div", { className: "flex items-center gap-2", children: (0, jsx_runtime_1.jsx)("span", { className: "text-b3-grey font-neue-montreal-semibold", children: "Ethereum" }) }), (0, jsx_runtime_1.jsxs)("div", { className: "text-b3-foreground-muted font-neue-montreal-medium text-sm", children: [eoaNativeBalance?.formattedTotal || "0.00", " ETH"] })] })] }), (0, jsx_runtime_1.jsxs)("div", { className: "text-right", children: [(0, jsx_runtime_1.jsxs)("div", { className: "text-b3-grey font-neue-montreal-semibold", children: ["$", eoaNativeBalance?.formattedTotalUsd || "0.00"] }), (0, jsx_runtime_1.jsx)("div", { className: "flex items-center gap-2", children: eoaNativeBalance?.priceChange24h !== null && eoaNativeBalance?.priceChange24h !== undefined ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Triangle, { className: `size-3 ${eoaNativeBalance.priceChange24h >= 0 ? "text-b3-positive fill-b3-positive" : "text-b3-negative fill-b3-negative rotate-180"}` }), (0, jsx_runtime_1.jsxs)("span", { className: `font-neue-montreal-medium text-sm ${eoaNativeBalance.priceChange24h >= 0 ? "text-b3-positive" : "text-b3-negative"}`, children: [eoaNativeBalance.priceChange24h >= 0 ? "+" : "", eoaNativeBalance.priceChange24h.toFixed(2), "%"] })] })) : ((0, jsx_runtime_1.jsx)("span", { className: "text-b3-foreground-muted font-neue-montreal-medium text-sm", children: "--" })) })] })] })] })), (0, jsx_runtime_1.jsxs)("div", { className: "border-b3-line flex items-center justify-between rounded-2xl border p-4", children: [(0, jsx_runtime_1.jsxs)("div", { className: "", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2", children: [(0, jsx_runtime_1.jsx)("img", { src: "https://cdn.b3.fun/b3_logo.svg", alt: "B3", className: "h-4" }), (0, jsx_runtime_1.jsx)("h3", { className: "font-neue-montreal-semibold text-b3-grey", children: "Global Account" })] }), (0, jsx_runtime_1.jsx)("p", { className: "text-b3-foreground-muted font-neue-montreal-medium mt-2 text-sm", children: "Your universal account for all B3 apps" })] }), (0, jsx_runtime_1.jsx)("button", { className: "text-b3-grey hover:text-b3-grey/80 hover:bg-b3-line border-b3-line flex size-12 items-center justify-center rounded-full border", onClick: onLogoutEnhanced, children: logoutLoading ? (0, jsx_runtime_1.jsx)(lucide_react_1.Loader2, { className: "animate-spin" }) : (0, jsx_runtime_1.jsx)(SignOutIcon_1.SignOutIcon, { size: 16, className: "text-b3-grey" }) })] })] }));
89
88
  };
90
- const TokensContent = () => ((0, jsx_runtime_1.jsxs)("div", { className: "space-y-4", children: [(0, jsx_runtime_1.jsx)("h3", { className: "text-b3-grey font-neue-montreal-semibold text-xl", children: "My Tokens" }), (0, jsx_runtime_1.jsxs)("div", { className: "space-y-3", 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-3", children: [(0, jsx_runtime_1.jsx)("div", { className: "flex h-10 w-10 items-center justify-center rounded-full", children: (0, jsx_runtime_1.jsx)("img", { src: "https://cdn.b3.fun/b3-coin-3d.png", alt: "B3", className: "size-10" }) }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("div", { className: "flex items-center gap-2", children: (0, jsx_runtime_1.jsx)("span", { className: "text-b3-grey font-neue-montreal-semibold", children: "B3" }) }), (0, jsx_runtime_1.jsx)("div", { className: "text-b3-foreground-muted font-neue-montreal-medium text-sm", children: "B3 Token" })] })] }), (0, jsx_runtime_1.jsxs)("div", { className: "text-right", children: [(0, jsx_runtime_1.jsx)("div", { className: "text-b3-grey font-neue-montreal-semibold", children: b3Balance?.formattedTotal || "0.00" }), (0, jsx_runtime_1.jsxs)("div", { className: "text-b3-foreground-muted font-neue-montreal-medium text-sm", children: ["$", b3Balance?.balanceUsdFormatted || "0.00"] })] })] }), (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-between", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-3", children: [(0, jsx_runtime_1.jsx)("div", { className: "flex h-10 w-10 items-center justify-center rounded-full", children: (0, jsx_runtime_1.jsx)("img", { src: "https://cdn.b3.fun/ethereum.svg", alt: "ETH", className: "size-10" }) }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("div", { className: "flex items-center gap-2", children: (0, jsx_runtime_1.jsx)("span", { className: "text-b3-grey font-neue-montreal-semibold", children: "Ethereum" }) }), (0, jsx_runtime_1.jsx)("div", { className: "text-b3-foreground-muted font-neue-montreal-medium text-sm", children: "ETH" })] })] }), (0, jsx_runtime_1.jsxs)("div", { className: "text-right", children: [(0, jsx_runtime_1.jsx)("div", { className: "text-b3-grey font-neue-montreal-semibold", children: nativeBalance?.formattedTotal || "0.00" }), (0, jsx_runtime_1.jsxs)("div", { className: "text-b3-foreground-muted font-neue-montreal-medium text-sm", children: ["$", nativeBalance?.formattedTotalUsd || "0.00"] })] })] })] })] }));
91
- const AssetsContent = () => ((0, jsx_runtime_1.jsx)("div", { className: "grid grid-cols-3 gap-4", children: nfts?.nftResponse ? ((0, jsx_runtime_1.jsx)(AccountAssets_1.AccountAssets, { nfts: nfts.nftResponse, isLoading: isLoading })) : ((0, jsx_runtime_1.jsx)("div", { className: "col-span-3 py-12 text-center text-gray-500", children: "No NFTs found" })) }));
92
89
  const AppsContent = () => ((0, jsx_runtime_1.jsxs)("div", { className: "space-y-4", children: [signers?.map((signer) => ((0, jsx_runtime_1.jsx)("div", { className: "rounded-xl border border-gray-200 p-4 dark:border-gray-800", children: (0, jsx_runtime_1.jsxs)("div", { className: "flex items-start justify-between", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-start gap-4", children: [(0, jsx_runtime_1.jsx)("div", { className: "flex h-10 w-10 items-center justify-center rounded-full bg-gray-100 dark:bg-gray-800", children: (0, jsx_runtime_1.jsx)("span", { className: "text-xs font-medium text-gray-600 dark:text-gray-400", children: "App" }) }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("h3", { className: "font-medium text-gray-900 dark:text-white", children: signer.partner.name }), (0, jsx_runtime_1.jsxs)("div", { className: "mt-2 space-y-1", children: [(0, jsx_runtime_1.jsxs)("p", { className: "text-xs text-gray-500", children: ["Added ", new Date(signer.createdAt).toLocaleDateString()] }), (0, jsx_runtime_1.jsxs)("p", { className: "text-xs text-gray-500", children: ["Expires ", new Date(Number(signer.endTimestamp) * 1000).toLocaleDateString()] }), (0, jsx_runtime_1.jsxs)("p", { className: "text-xs text-gray-500", children: ["Max spend: ", (0, formatNumber_1.formatNumber)(Number((0, viem_1.formatUnits)(signer.nativeTokenLimitPerTransaction, 18))), " ETH"] })] })] })] }), (0, jsx_runtime_1.jsx)(react_1.Button, { variant: "outline", size: "sm", className: "border-red-200 text-red-500 hover:border-red-300 hover:text-red-600", onClick: () => handleRevoke(signer), disabled: revokingSignerId === signer.id, children: revokingSignerId === signer.id ? "Revoking..." : "Revoke" })] }) }, signer.id))), !signers?.length && (0, jsx_runtime_1.jsx)("div", { className: "py-12 text-center text-gray-500", children: "No connected apps" })] }));
93
90
  const SettingsContent = () => {
94
91
  const [unlinkingAccountId, setUnlinkingAccountId] = (0, react_2.useState)(null);
@@ -138,5 +135,5 @@ function ManageAccount({ onLogout, onSwap: _onSwap, onDeposit: _onDeposit, chain
138
135
  if (["overview", "tokens", "nfts", "apps", "settings"].includes(tab)) {
139
136
  setActiveTab?.(tab);
140
137
  }
141
- }, children: [(0, jsx_runtime_1.jsx)("div", { className: "px-4", children: (0, jsx_runtime_1.jsxs)(react_1.TabsListPrimitive, { className: "grid h-auto grid-cols-2 grid-rows-2 gap-3 rounded-none border-none bg-transparent", children: [(0, jsx_runtime_1.jsxs)(react_1.TabTriggerPrimitive, { value: "overview", className: "data-[state=active]:bg-b3-primary-blue data-[state=active]:hover:bg-b3-primary-blue data-[state=active]:border-b3-primary-blue group flex h-12 w-full items-center justify-center gap-2 rounded-xl border border-gray-200 bg-white p-2 text-center shadow-sm transition-all duration-200 hover:bg-gray-50 hover:shadow-md data-[state=active]:shadow-lg", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.BarChart3, { size: 20, className: "text-b3-primary-blue shrink-0 group-data-[state=active]:text-white" }), (0, jsx_runtime_1.jsx)("span", { className: "text-b3-grey font-neue-montreal-semibold text-sm group-data-[state=active]:text-white", children: "Overview" })] }), (0, jsx_runtime_1.jsxs)(react_1.TabTriggerPrimitive, { value: "tokens", className: "data-[state=active]:bg-b3-primary-blue data-[state=active]:hover:bg-b3-primary-blue data-[state=active]:border-b3-primary-blue group flex h-12 w-full items-center justify-center gap-2 rounded-xl border border-gray-200 bg-white p-2 text-center shadow-sm transition-all duration-200 hover:bg-gray-50 hover:shadow-md data-[state=active]:shadow-lg", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Coins, { size: 20, className: "text-b3-primary-blue shrink-0 group-data-[state=active]:text-white" }), (0, jsx_runtime_1.jsx)("span", { className: "text-b3-grey font-neue-montreal-semibold text-sm group-data-[state=active]:text-white", children: "Tokens" })] }), (0, jsx_runtime_1.jsxs)(react_1.TabTriggerPrimitive, { value: "nfts", className: "data-[state=active]:bg-b3-primary-blue data-[state=active]:hover:bg-b3-primary-blue data-[state=active]:border-b3-primary-blue group flex h-12 w-full items-center justify-center gap-2 rounded-xl border border-gray-200 bg-white p-2 text-center shadow-sm transition-all duration-200 hover:bg-gray-50 hover:shadow-md data-[state=active]:shadow-lg", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Image, { size: 20, className: "text-b3-primary-blue shrink-0 group-data-[state=active]:text-white" }), (0, jsx_runtime_1.jsx)("span", { className: "text-b3-grey font-neue-montreal-semibold text-sm group-data-[state=active]:text-white", children: "NFTs" })] }), (0, jsx_runtime_1.jsxs)(react_1.TabTriggerPrimitive, { value: "settings", className: "data-[state=active]:bg-b3-primary-blue data-[state=active]:hover:bg-b3-primary-blue data-[state=active]:border-b3-primary-blue group flex h-12 w-full items-center justify-center gap-2 rounded-xl border border-gray-200 bg-white p-2 text-center shadow-sm transition-all duration-200 hover:bg-gray-50 hover:shadow-md data-[state=active]:shadow-lg", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Settings, { size: 20, className: "text-b3-primary-blue shrink-0 group-data-[state=active]:text-white" }), (0, jsx_runtime_1.jsx)("span", { className: "text-b3-grey font-neue-montreal-semibold text-sm group-data-[state=active]:text-white", children: "Settings" })] })] }) }), (0, jsx_runtime_1.jsx)(react_1.TabsContentPrimitive, { value: "overview", className: "px-4 pb-4 pt-2", children: (0, jsx_runtime_1.jsx)(BalanceContent, {}) }), (0, jsx_runtime_1.jsx)(react_1.TabsContentPrimitive, { value: "tokens", className: "px-4 pb-4 pt-2", children: (0, jsx_runtime_1.jsx)(TokensContent, {}) }), (0, jsx_runtime_1.jsx)(react_1.TabsContentPrimitive, { value: "nfts", className: "px-4 pb-4 pt-2", children: (0, jsx_runtime_1.jsx)(AssetsContent, {}) }), (0, jsx_runtime_1.jsx)(react_1.TabsContentPrimitive, { value: "apps", className: "px-4 pb-4 pt-2", children: (0, jsx_runtime_1.jsx)(AppsContent, {}) }), (0, jsx_runtime_1.jsx)(react_1.TabsContentPrimitive, { value: "settings", className: "px-4 pb-4 pt-2", children: (0, jsx_runtime_1.jsx)(SettingsContent, {}) })] }) }) }));
138
+ }, children: [(0, jsx_runtime_1.jsx)("div", { className: "px-4", children: (0, jsx_runtime_1.jsxs)(react_1.TabsListPrimitive, { className: "grid h-auto grid-cols-2 grid-rows-2 gap-3 rounded-none border-none bg-transparent", children: [(0, jsx_runtime_1.jsxs)(react_1.TabTriggerPrimitive, { value: "overview", className: "data-[state=active]:bg-b3-primary-blue data-[state=active]:hover:bg-b3-primary-blue data-[state=active]:border-b3-primary-blue group flex h-12 w-full items-center justify-center gap-2 rounded-xl border border-gray-200 bg-white p-2 text-center shadow-sm transition-all duration-200 hover:bg-gray-50 hover:shadow-md data-[state=active]:shadow-lg", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.BarChart3, { size: 20, className: "text-b3-primary-blue shrink-0 group-data-[state=active]:text-white" }), (0, jsx_runtime_1.jsx)("span", { className: "text-b3-grey font-neue-montreal-semibold text-sm group-data-[state=active]:text-white", children: "Overview" })] }), (0, jsx_runtime_1.jsxs)(react_1.TabTriggerPrimitive, { value: "tokens", className: "data-[state=active]:bg-b3-primary-blue data-[state=active]:hover:bg-b3-primary-blue data-[state=active]:border-b3-primary-blue group flex h-12 w-full items-center justify-center gap-2 rounded-xl border border-gray-200 bg-white p-2 text-center shadow-sm transition-all duration-200 hover:bg-gray-50 hover:shadow-md data-[state=active]:shadow-lg", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Coins, { size: 20, className: "text-b3-primary-blue shrink-0 group-data-[state=active]:text-white" }), (0, jsx_runtime_1.jsx)("span", { className: "text-b3-grey font-neue-montreal-semibold text-sm group-data-[state=active]:text-white", children: "Tokens" })] }), (0, jsx_runtime_1.jsxs)(react_1.TabTriggerPrimitive, { value: "nfts", className: "data-[state=active]:bg-b3-primary-blue data-[state=active]:hover:bg-b3-primary-blue data-[state=active]:border-b3-primary-blue group flex h-12 w-full items-center justify-center gap-2 rounded-xl border border-gray-200 bg-white p-2 text-center shadow-sm transition-all duration-200 hover:bg-gray-50 hover:shadow-md data-[state=active]:shadow-lg", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Image, { size: 20, className: "text-b3-primary-blue shrink-0 group-data-[state=active]:text-white" }), (0, jsx_runtime_1.jsx)("span", { className: "text-b3-grey font-neue-montreal-semibold text-sm group-data-[state=active]:text-white", children: "NFTs" })] }), (0, jsx_runtime_1.jsxs)(react_1.TabTriggerPrimitive, { value: "settings", className: "data-[state=active]:bg-b3-primary-blue data-[state=active]:hover:bg-b3-primary-blue data-[state=active]:border-b3-primary-blue group flex h-12 w-full items-center justify-center gap-2 rounded-xl border border-gray-200 bg-white p-2 text-center shadow-sm transition-all duration-200 hover:bg-gray-50 hover:shadow-md data-[state=active]:shadow-lg", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Settings, { size: 20, className: "text-b3-primary-blue shrink-0 group-data-[state=active]:text-white" }), (0, jsx_runtime_1.jsx)("span", { className: "text-b3-grey font-neue-montreal-semibold text-sm group-data-[state=active]:text-white", children: "Settings" })] })] }) }), (0, jsx_runtime_1.jsx)(react_1.TabsContentPrimitive, { value: "overview", className: "px-4 pb-4 pt-2", children: (0, jsx_runtime_1.jsx)(BalanceContent, {}) }), (0, jsx_runtime_1.jsx)(react_1.TabsContentPrimitive, { value: "tokens", className: "px-4 pb-4 pt-2", children: (0, jsx_runtime_1.jsx)(ContentTokens_1.ContentTokens, { activeTab: activeTab }) }), (0, jsx_runtime_1.jsx)(react_1.TabsContentPrimitive, { value: "nfts", className: "px-4 pb-4 pt-2", children: (0, jsx_runtime_1.jsx)("div", { className: "grid grid-cols-3 gap-4", children: nfts?.nftResponse ? ((0, jsx_runtime_1.jsx)(AccountAssets_1.AccountAssets, { nfts: nfts.nftResponse, isLoading: isLoading })) : ((0, jsx_runtime_1.jsx)("div", { className: "col-span-3 py-12 text-center text-gray-500", children: "No NFTs found" })) }) }), (0, jsx_runtime_1.jsx)(react_1.TabsContentPrimitive, { value: "apps", className: "px-4 pb-4 pt-2", children: (0, jsx_runtime_1.jsx)(AppsContent, {}) }), (0, jsx_runtime_1.jsx)(react_1.TabsContentPrimitive, { value: "settings", className: "px-4 pb-4 pt-2", children: (0, jsx_runtime_1.jsx)(SettingsContent, {}) })] }) }) }));
142
139
  }
@@ -18,6 +18,7 @@ export { useIsomorphicLayoutEffect } from "./useIsomorphicLayoutEffect";
18
18
  export { useMediaQuery } from "./useMediaQuery";
19
19
  export { useNativeBalance, useNativeBalanceFromRPC } from "./useNativeBalance";
20
20
  export { useOneBalance } from "./useOneBalance";
21
+ export { useSimBalance } from "./useSimBalance";
21
22
  export { useProfile, useProfilePreference, type CombinedProfile, type PreferenceRequestBody, type Profile, } from "./useProfile";
22
23
  export { useQueryB3 } from "./useQueryB3";
23
24
  export { useQueryBSMNT } from "./useQueryBSMNT";
@@ -14,7 +14,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
- exports.useURLParams = exports.useUnifiedChainSwitchAndExecute = exports.useTokensFromAddress = exports.useTokenPriceWithFallback = exports.useTokenPrice = exports.useTokenFromUrl = exports.useTokenData = exports.useTokenBalancesByChain = exports.useTokenBalance = exports.useSiwe = exports.useSearchParamsSSR = exports.useRouter = exports.useRemoveSessionKey = exports.useQueryBSMNT = exports.useQueryB3 = exports.useProfilePreference = exports.useProfile = exports.useOneBalance = exports.useNativeBalanceFromRPC = exports.useNativeBalance = exports.useMediaQuery = exports.useIsomorphicLayoutEffect = exports.useIsMobile = exports.useHasMounted = exports.useHandleConnectWithPrivy = exports.useGetGeo = exports.useGetAllTWSigners = exports.useExchangeRate = exports.useConnect = exports.useChainSwitchWithAction = exports.useBestTransactionPath = exports.useB3EnsName = exports.useB3BalanceFromAddresses = exports.useAuthentication = exports.useAddTWSessionKey = exports.useAccountWallet = exports.useAccountAssets = void 0;
17
+ exports.useURLParams = exports.useUnifiedChainSwitchAndExecute = exports.useTokensFromAddress = exports.useTokenPriceWithFallback = exports.useTokenPrice = exports.useTokenFromUrl = exports.useTokenData = exports.useTokenBalancesByChain = exports.useTokenBalance = exports.useSiwe = exports.useSearchParamsSSR = exports.useRouter = exports.useRemoveSessionKey = exports.useQueryBSMNT = exports.useQueryB3 = exports.useProfilePreference = exports.useProfile = exports.useSimBalance = exports.useOneBalance = exports.useNativeBalanceFromRPC = exports.useNativeBalance = exports.useMediaQuery = exports.useIsomorphicLayoutEffect = exports.useIsMobile = exports.useHasMounted = exports.useHandleConnectWithPrivy = exports.useGetGeo = exports.useGetAllTWSigners = exports.useExchangeRate = exports.useConnect = exports.useChainSwitchWithAction = exports.useBestTransactionPath = exports.useB3EnsName = exports.useB3BalanceFromAddresses = exports.useAuthentication = exports.useAddTWSessionKey = exports.useAccountWallet = exports.useAccountAssets = void 0;
18
18
  var useAccountAssets_1 = require("./useAccountAssets");
19
19
  Object.defineProperty(exports, "useAccountAssets", { enumerable: true, get: function () { return useAccountAssets_1.useAccountAssets; } });
20
20
  var useAccountWallet_1 = require("./useAccountWallet");
@@ -55,6 +55,8 @@ Object.defineProperty(exports, "useNativeBalance", { enumerable: true, get: func
55
55
  Object.defineProperty(exports, "useNativeBalanceFromRPC", { enumerable: true, get: function () { return useNativeBalance_1.useNativeBalanceFromRPC; } });
56
56
  var useOneBalance_1 = require("./useOneBalance");
57
57
  Object.defineProperty(exports, "useOneBalance", { enumerable: true, get: function () { return useOneBalance_1.useOneBalance; } });
58
+ var useSimBalance_1 = require("./useSimBalance");
59
+ Object.defineProperty(exports, "useSimBalance", { enumerable: true, get: function () { return useSimBalance_1.useSimBalance; } });
58
60
  var useProfile_1 = require("./useProfile");
59
61
  Object.defineProperty(exports, "useProfile", { enumerable: true, get: function () { return useProfile_1.useProfile; } });
60
62
  Object.defineProperty(exports, "useProfilePreference", { enumerable: true, get: function () { return useProfile_1.useProfilePreference; } });
@@ -0,0 +1,24 @@
1
+ export interface SimTokenMetadata {
2
+ logo?: string;
3
+ }
4
+ export interface SimBalanceItem {
5
+ chain: string;
6
+ chain_id: number;
7
+ address: string;
8
+ amount: string;
9
+ symbol: string;
10
+ decimals: number;
11
+ price_usd?: number;
12
+ value_usd?: number;
13
+ name?: string;
14
+ token_metadata?: SimTokenMetadata;
15
+ pool_size?: number;
16
+ low_liquidity?: boolean;
17
+ }
18
+ export interface SimBalanceResponse {
19
+ request_time: string;
20
+ response_time: string;
21
+ wallet_address: string;
22
+ balances: SimBalanceItem[];
23
+ }
24
+ export declare function useSimBalance(address?: string): import("@tanstack/react-query").UseQueryResult<SimBalanceResponse, Error>;
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useSimBalance = useSimBalance;
4
+ const react_query_1 = require("@tanstack/react-query");
5
+ async function fetchSimBalance(address) {
6
+ if (!address)
7
+ throw new Error("Address is required");
8
+ const response = await fetch(`https://simdune-api.sean-430.workers.dev/?url=https://api.sim.dune.com/v1/evm/balances/${address}?metadata=logo&localkey=${process.env.PUBLIC_LOCAL_KEY}`);
9
+ if (!response.ok) {
10
+ throw new Error(`Failed to fetch balance: ${response.statusText}`);
11
+ }
12
+ const balanceData = await response.json();
13
+ return balanceData;
14
+ }
15
+ function useSimBalance(address) {
16
+ return (0, react_query_1.useQuery)({
17
+ queryKey: ["simBalance", address],
18
+ queryFn: () => {
19
+ if (!address)
20
+ throw new Error("Address is required");
21
+ return fetchSimBalance(address);
22
+ },
23
+ enabled: Boolean(address),
24
+ });
25
+ }
@@ -110,7 +110,6 @@ function useUnifiedChainSwitchAndExecute() {
110
110
  try {
111
111
  setIsSwitchingOrExecuting(true);
112
112
  const chain = (0, supported_1.getThirdwebChain)(targetChainId);
113
- sonner_1.toast.info("Preparing transaction…");
114
113
  const transaction = (0, thirdweb_2.prepareTransaction)({
115
114
  client: thirdweb_1.client,
116
115
  chain,
@@ -1,5 +1,5 @@
1
1
  export declare function ChainTokenIcon({ chainUrl, tokenUrl, className, }: {
2
2
  chainUrl: string;
3
- tokenUrl: string;
3
+ tokenUrl?: string;
4
4
  className?: string;
5
5
  }): import("react/jsx-runtime").JSX.Element;
@@ -1,4 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { HelpCircle } from "lucide-react";
2
3
  export function ChainTokenIcon({ chainUrl, tokenUrl, className = "", }) {
3
- return (_jsxs("div", { className: `relative ${className}`, children: [_jsx("img", { src: tokenUrl, alt: "Token", className: "h-full w-full rounded-full" }), _jsx("div", { className: "bg-as-on-surface-1 border-as-stroke absolute bottom-0 right-0 h-[45%] w-[45%] rounded border", children: _jsx("img", { src: chainUrl, alt: "Chain", className: "h-full w-full rounded" }) })] }));
4
+ return (_jsxs("div", { className: `relative ${className}`, children: [tokenUrl ? (_jsx("img", { src: tokenUrl, alt: "Token", className: "h-full w-full rounded-full" })) : (_jsx(HelpCircle, { className: "text-b3-react-foreground h-full w-full" })), _jsx("div", { className: "bg-as-on-surface-1 border-as-stroke absolute bottom-0 right-0 h-[45%] w-[45%] rounded border", children: _jsx("img", { src: chainUrl, alt: "Chain", className: "h-full w-full rounded" }) })] }));
4
5
  }
@@ -0,0 +1,14 @@
1
+ interface ContentTokensProps {
2
+ activeTab: string;
3
+ }
4
+ /**
5
+ * ContentTokens Component
6
+ *
7
+ * Displays user's token balances with ability to send tokens. Features:
8
+ * - Animated transitions between token list and send form
9
+ * - Smart filtering (shows all tokens when ≤5 valuable tokens or no $1+ tokens)
10
+ * - NumericFormat inputs for proper number handling
11
+ * - Focus preservation during transitions (see render functions pattern below)
12
+ */
13
+ export declare function ContentTokens({ activeTab }: ContentTokensProps): import("react/jsx-runtime").JSX.Element;
14
+ export {};