@algobright/solana-connector 0.1.1 → 0.1.3

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.
@@ -36,9 +36,10 @@ __export(WalletDropdown_exports, {
36
36
  module.exports = __toCommonJS(WalletDropdown_exports);
37
37
 
38
38
  // src/components/WalletDropdown/WalletDropdown.tsx
39
- var import_react2 = require("react");
40
- var import_WalletDropdown = __toESM(require("./WalletDropdown.module-A6KHWKGK.module.css"));
41
- var import_react3 = require("motion/react");
39
+ var import_react3 = require("react");
40
+ var import_kit2 = require("@solana/kit");
41
+ var import_WalletDropdown = __toESM(require("./WalletDropdown.module-DOK7CUOQ.module.css"));
42
+ var import_react4 = require("motion/react");
42
43
 
43
44
  // src/components/shared/Avatar/Avatar.tsx
44
45
  var import_react = require("react");
@@ -72,11 +73,11 @@ var Avatar_default = Avatar;
72
73
  var import_lucide_react2 = require("lucide-react");
73
74
 
74
75
  // src/components/shared/Button/Button.tsx
75
- var React = __toESM(require("react"));
76
+ var import_react2 = require("react");
76
77
  var import_button = require("@base-ui/react/button");
77
- var import_Button = __toESM(require("./Button.module-HQNNX6IB.module.css"));
78
+ var import_Button = __toESM(require("./Button.module-QCTUNBHA.module.css"));
78
79
  var import_jsx_runtime2 = require("react/jsx-runtime");
79
- var Button = React.forwardRef(
80
+ var Button = (0, import_react2.forwardRef)(
80
81
  ({ className, variant = "default", size = "default", ...props }, ref) => {
81
82
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
82
83
  import_button.Button,
@@ -97,9 +98,68 @@ var Button_default = Button;
97
98
  var Button_default2 = Button_default;
98
99
 
99
100
  // src/components/WalletDropdown/WalletDropdown.tsx
101
+ var import_connector2 = require("@solana/connector");
102
+ var import_clsx = require("clsx");
103
+
104
+ // src/utils/fetchBalance.tsx
105
+ var import_token = require("@solana-program/token");
100
106
  var import_connector = require("@solana/connector");
101
107
  var import_kit = require("@solana/kit");
102
- var import_clsx = require("clsx");
108
+ async function getSolBalance(client, pubkey) {
109
+ let balance = 0;
110
+ try {
111
+ const rpcUrl = client.getRpcUrl();
112
+ if (!rpcUrl) {
113
+ console.error("RPC URL is not available from the ConnectorClient.");
114
+ return 0;
115
+ }
116
+ const pubkeyAddress = (0, import_connector.address)(pubkey);
117
+ const rpc = (0, import_kit.createSolanaRpc)(rpcUrl);
118
+ const balanceResponse = await rpc.getBalance(pubkeyAddress).send();
119
+ balance = (0, import_connector.lamportsToSol)(balanceResponse.value);
120
+ } catch (error) {
121
+ console.error("Error fetching SOL balance:", error);
122
+ } finally {
123
+ return balance;
124
+ }
125
+ }
126
+ async function getTokenBalance(client, pubkey, mintAddress) {
127
+ var _a;
128
+ let balance = 0;
129
+ try {
130
+ const rpcUrl = client.getRpcUrl();
131
+ if (!rpcUrl) {
132
+ console.error("RPC URL is not available from the ConnectorClient.");
133
+ return 0;
134
+ }
135
+ const pubkeyAddress = (0, import_connector.address)(pubkey);
136
+ const mintPubkey = (0, import_connector.address)(mintAddress);
137
+ const rpc = (0, import_kit.createSolanaRpc)(rpcUrl);
138
+ const mintInfo = await rpc.getAccountInfo(
139
+ mintPubkey,
140
+ { encoding: "base64" }
141
+ ).send();
142
+ const ownerProgram = (_a = mintInfo.value) == null ? void 0 : _a.owner;
143
+ if (!ownerProgram) {
144
+ throw new Error("Failed to fetch mint account info");
145
+ }
146
+ const tokenProgram = (0, import_connector.address)(ownerProgram);
147
+ const [tokenPDA] = await (0, import_token.findAssociatedTokenPda)({
148
+ mint: mintPubkey,
149
+ owner: pubkeyAddress,
150
+ tokenProgram
151
+ });
152
+ const tokenBalance = await rpc.getTokenAccountBalance(tokenPDA).send();
153
+ if (tokenBalance.value) {
154
+ balance = parseFloat(tokenBalance.value.uiAmountString);
155
+ }
156
+ } catch (error) {
157
+ } finally {
158
+ return balance;
159
+ }
160
+ }
161
+
162
+ // src/components/WalletDropdown/WalletDropdown.tsx
103
163
  var import_jsx_runtime3 = require("react/jsx-runtime");
104
164
  var networkColor = {
105
165
  "solana:mainnet": "#00c950",
@@ -108,13 +168,26 @@ var networkColor = {
108
168
  "solana:localnet": "#ff3b3b"
109
169
  };
110
170
  function WalletDropdown(props) {
111
- const client = (0, import_connector.useConnectorClient)();
112
- const { CN_ConnectButton, selectedAccount, walletIcon, walletName, theme, allowNetworkSwitch, showSolBalance } = props;
113
- const [view, setView] = (0, import_react2.useState)("wallet");
114
- const [copied, setCopied] = (0, import_react2.useState)(false);
115
- const fetching = (0, import_react2.useRef)(false);
116
- const [isFetchingBalance, setIsFetchingBalance] = (0, import_react2.useState)(false);
117
- const [solBalance, setSolBalance] = (0, import_react2.useState)(null);
171
+ const client = (0, import_connector2.useConnectorClient)();
172
+ const {
173
+ CN_DropdownMenu,
174
+ theme = "light",
175
+ allowNetworkSwitch = true,
176
+ showSolBalance = true,
177
+ showDefaultToken
178
+ } = props;
179
+ const [view, setView] = (0, import_react3.useState)("wallet");
180
+ const [copied, setCopied] = (0, import_react3.useState)(false);
181
+ const { account, connector } = (0, import_connector2.useConnector)();
182
+ const fetchingSolBalance = (0, import_react3.useRef)(false);
183
+ const [isFetchingBalance, setIsFetchingBalance] = (0, import_react3.useState)(false);
184
+ const fetchingDefaultToken = (0, import_react3.useRef)(false);
185
+ const [isFetchingDefaultTokenBalance, setIsFetchingDefaultTokenBalance] = (0, import_react3.useState)(false);
186
+ const [solBalance, setSolBalance] = (0, import_react3.useState)(null);
187
+ const [defaultTokenBalance, setDefaultTokenBalance] = (0, import_react3.useState)(null);
188
+ const selectedAccount = account || "";
189
+ const walletName = (connector == null ? void 0 : connector.name) || "Unknown Wallet";
190
+ const walletIcon = (connector == null ? void 0 : connector.icon) || void 0;
118
191
  const shortAddress = `${selectedAccount.slice(0, 4)}...${selectedAccount.slice(-4)}`;
119
192
  async function handleCopy() {
120
193
  try {
@@ -127,38 +200,45 @@ function WalletDropdown(props) {
127
200
  }
128
201
  }
129
202
  async function fetchSolBalance() {
130
- if (!client || fetching.current) return;
203
+ if (!client || fetchingSolBalance.current) return;
131
204
  setIsFetchingBalance(true);
132
- fetching.current = true;
133
- try {
134
- const rpcUrl = client.getRpcUrl();
135
- const pubkey = (0, import_connector.address)(selectedAccount);
136
- if (!rpcUrl) throw new Error("No RPC endpoint configured");
137
- const rpc = (0, import_kit.createSolanaRpc)(rpcUrl);
138
- const solLamports = (await rpc.getBalance(pubkey).send()).value || 0;
139
- const sol = (0, import_connector.lamportsToSol)(solLamports);
140
- setSolBalance(sol);
141
- } catch (error) {
142
- setSolBalance(0);
143
- } finally {
144
- setIsFetchingBalance(false);
145
- fetching.current = false;
205
+ fetchingSolBalance.current = true;
206
+ const solBalance2 = await getSolBalance(client, selectedAccount);
207
+ setSolBalance(solBalance2);
208
+ setIsFetchingBalance(false);
209
+ fetchingSolBalance.current = false;
210
+ }
211
+ async function fetchDefaultTokenBalance() {
212
+ if (!showDefaultToken || !showDefaultToken.address || !client || fetchingDefaultToken.current) return;
213
+ const isValidAddress = (0, import_kit2.isAddress)(showDefaultToken.address);
214
+ if (!isValidAddress) {
215
+ console.error("Invalid default token address:", showDefaultToken);
216
+ return;
146
217
  }
218
+ setIsFetchingDefaultTokenBalance(true);
219
+ fetchingDefaultToken.current = true;
220
+ const tokenBalance = await getTokenBalance(client, selectedAccount, showDefaultToken.address);
221
+ setDefaultTokenBalance(tokenBalance);
222
+ setIsFetchingDefaultTokenBalance(false);
223
+ fetchingDefaultToken.current = false;
147
224
  }
148
- (0, import_react2.useEffect)(() => {
225
+ (0, import_react3.useEffect)(() => {
149
226
  if (showSolBalance && selectedAccount && client) {
150
227
  fetchSolBalance();
151
228
  }
152
- }, [selectedAccount, client, showSolBalance]);
229
+ if (showDefaultToken && selectedAccount && client) {
230
+ fetchDefaultTokenBalance();
231
+ }
232
+ }, [selectedAccount, client, showSolBalance, showDefaultToken]);
153
233
  if (view === "wallet") {
154
234
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
155
- import_react3.motion.div,
235
+ import_react4.motion.div,
156
236
  {
157
237
  initial: { opacity: 0 },
158
238
  animate: { opacity: 1 },
159
239
  exit: { opacity: 0 },
160
240
  transition: { duration: 0.2 },
161
- className: (0, import_clsx.clsx)(import_WalletDropdown.default.WalletDropdown, CN_ConnectButton),
241
+ className: (0, import_clsx.clsx)(import_WalletDropdown.default.WalletDropdown, CN_DropdownMenu),
162
242
  "data-theme": theme,
163
243
  children: [
164
244
  /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: import_WalletDropdown.default.Header, children: [
@@ -191,7 +271,7 @@ function WalletDropdown(props) {
191
271
  }
192
272
  ),
193
273
  allowNetworkSwitch && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
194
- import_connector.ClusterElement,
274
+ import_connector2.ClusterElement,
195
275
  {
196
276
  render: ({ cluster }) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
197
277
  Button_default2,
@@ -243,10 +323,38 @@ function WalletDropdown(props) {
243
323
  ]
244
324
  }
245
325
  ),
246
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: import_WalletDropdown.default.balanceValue, children: isFetchingBalance ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: import_WalletDropdown.default.balanceLoading }) : solBalance !== null ? `${solBalance.toFixed(4)} SOL` : "-- SOL" })
326
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: import_WalletDropdown.default.balanceValue, title: String(solBalance) || "0", children: isFetchingBalance ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: import_WalletDropdown.default.balanceLoading }) : solBalance !== null ? `${solBalance.toFixed(4)} SOL` : "-- SOL" })
327
+ ] }),
328
+ showDefaultToken && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: import_WalletDropdown.default.balanceSection, children: [
329
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
330
+ "div",
331
+ {
332
+ className: import_WalletDropdown.default.balanceHeader,
333
+ children: [
334
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: import_WalletDropdown.default.balanceLabel, children: "Balance" }),
335
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
336
+ "button",
337
+ {
338
+ onClick: () => fetchDefaultTokenBalance(),
339
+ disabled: isFetchingDefaultTokenBalance,
340
+ title: "Refresh balance",
341
+ className: import_WalletDropdown.default.refreshButton,
342
+ "data-loading": isFetchingDefaultTokenBalance,
343
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
344
+ import_lucide_react2.RefreshCw,
345
+ {
346
+ className: import_WalletDropdown.default.refreshIcon
347
+ }
348
+ )
349
+ }
350
+ )
351
+ ]
352
+ }
353
+ ),
354
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: import_WalletDropdown.default.balanceValue, title: String(defaultTokenBalance) || "0", children: isFetchingDefaultTokenBalance ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: import_WalletDropdown.default.balanceLoading }) : defaultTokenBalance !== null ? `${defaultTokenBalance.toFixed(4)} ${(showDefaultToken == null ? void 0 : showDefaultToken.symbol) || ""}` : `-- ${(showDefaultToken == null ? void 0 : showDefaultToken.symbol) || ""}` })
247
355
  ] }),
248
356
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
249
- import_connector.DisconnectElement,
357
+ import_connector2.DisconnectElement,
250
358
  {
251
359
  render: ({ disconnect, disconnecting }) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
252
360
  Button_default2,
@@ -268,15 +376,14 @@ function WalletDropdown(props) {
268
376
  );
269
377
  }
270
378
  if (view === "network") {
271
- console.count("Network view rendered");
272
379
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
273
- import_react3.motion.div,
380
+ import_react4.motion.div,
274
381
  {
275
382
  initial: { opacity: 0 },
276
383
  animate: { opacity: 1 },
277
384
  exit: { opacity: 0 },
278
385
  transition: { duration: 0.2 },
279
- className: (0, import_clsx.clsx)(import_WalletDropdown.default.WalletDropdown, CN_ConnectButton),
386
+ className: (0, import_clsx.clsx)(import_WalletDropdown.default.WalletDropdown, CN_DropdownMenu),
280
387
  "data-theme": theme,
281
388
  children: [
282
389
  /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: import_WalletDropdown.default.NetworkHeader, children: [
@@ -295,11 +402,11 @@ function WalletDropdown(props) {
295
402
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: "Network Settings" })
296
403
  ] }),
297
404
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
298
- import_connector.ClusterElement,
405
+ import_connector2.ClusterElement,
299
406
  {
300
407
  render: ({ cluster, clusters, setCluster }) => {
301
408
  const currentClusterId = (cluster == null ? void 0 : cluster.id) || "solana:mainnet";
302
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: import_WalletDropdown.default.networkOptions, children: clusters.map((network, index) => {
409
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: import_WalletDropdown.default.networkOptions, children: clusters.map((network) => {
303
410
  const isSelected = currentClusterId === network.id;
304
411
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
305
412
  "div",
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/components/WalletDropdown/index.ts","../src/components/WalletDropdown/WalletDropdown.tsx","../src/components/shared/Avatar/Avatar.tsx","../src/components/shared/Avatar/index.ts","../src/components/shared/Button/Button.tsx","../src/components/shared/Button/index.ts"],"sourcesContent":["import WalletDropdown from './WalletDropdown';\n\n\nexport default WalletDropdown","import { useEffect, useRef, useState } from 'react'\nimport styles from './WalletDropdown.module.css'\nimport { motion } from 'motion/react';\nimport Avatar from '@shared/Avatar';\nimport { Check, ChevronLeft, Copy, Globe, LogOut, RefreshCw } from 'lucide-react';\nimport Button from '@shared/Button';\nimport { address, BalanceElement, ClusterElement, DisconnectElement, lamportsToSol, useCluster, useConnectorClient } from '@solana/connector';\nimport { createSolanaRpc } from '@solana/kit';\nimport { clsx } from 'clsx';\n\ninterface WalletDropdownProps {\n CN_ConnectButton?: string;\n selectedAccount: string;\n walletIcon?: string;\n walletName: string;\n theme?: 'light' | 'dark';\n\n allowNetworkSwitch?: boolean;\n showSolBalance?: boolean;\n}\n\ntype DropdownView = 'wallet' | 'network';\n\nconst networkColor: Record<string, string> = {\n 'solana:mainnet': '#00c950',\n 'solana:devnet': '#2b7fff',\n 'solana:testnet': '#f0b100',\n 'solana:localnet': '#ff3b3b',\n};\n\nexport function WalletDropdown(props: WalletDropdownProps) {\n const client = useConnectorClient();\n\n const { CN_ConnectButton, selectedAccount, walletIcon, walletName, theme, allowNetworkSwitch, showSolBalance } = props\n\n const [view, setView] = useState<DropdownView>('wallet');\n const [copied, setCopied] = useState(false);\n\n const fetching = useRef(false);\n const [isFetchingBalance, setIsFetchingBalance] = useState(false);\n const [solBalance, setSolBalance] = useState<number | null>(null);\n\n const shortAddress = `${selectedAccount.slice(0, 4)}...${selectedAccount.slice(-4)}`;\n\n async function handleCopy() {\n try {\n await navigator.clipboard.writeText(selectedAccount);\n setCopied(true);\n setTimeout(() => setCopied(false), 2000);\n } catch (error) {\n setCopied(false);\n console.error('Failed to copy to clipboard:', error);\n }\n }\n\n async function fetchSolBalance() {\n if (!client || fetching.current) return;\n setIsFetchingBalance(true);\n fetching.current = true;\n try {\n const rpcUrl = client.getRpcUrl();\n const pubkey = address(selectedAccount);\n if (!rpcUrl) throw new Error('No RPC endpoint configured');\n const rpc = createSolanaRpc(rpcUrl);\n const solLamports = (await rpc.getBalance(pubkey).send()).value || 0;\n const sol = lamportsToSol(solLamports);\n setSolBalance(sol);\n\n } catch (error) {\n setSolBalance(0);\n } finally {\n setIsFetchingBalance(false);\n fetching.current = false;\n }\n }\n\n useEffect(() => {\n if (showSolBalance && selectedAccount && client) {\n fetchSolBalance();\n }\n }, [selectedAccount, client, showSolBalance]);\n\n if (view === 'wallet') {\n return (\n <motion.div\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={{ duration: 0.2 }}\n className={clsx(styles.WalletDropdown, CN_ConnectButton)}\n data-theme={theme}\n >\n {/* Header with Avatar and Address */}\n <div className={styles.Header}>\n <div className={styles.addressAndAvatar}>\n <Avatar\n width={48}\n height={48}\n src={walletIcon}\n alt={walletName}\n />\n <div className={styles.address}>\n <span className={styles.shortAddress}>{shortAddress}</span>\n <span className={styles.walletName}>{walletName}</span>\n </div>\n </div>\n\n <div className={styles.actions}>\n <Button\n type=\"button\"\n onClick={handleCopy}\n variant=\"outline\"\n size=\"icon\"\n className=\"rounded-full\"\n title={copied ? 'Copied!' : 'Copy address'}\n >\n {copied ?\n <Check className={styles.checkIcon} /> :\n <Copy />\n }\n </Button>\n\n {/* Network Selector Globe Button */}\n {allowNetworkSwitch && (\n <ClusterElement\n render={({ cluster }) => (\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"icon\"\n onClick={() => setView('network')}\n title={`Network: ${cluster?.label || 'Unknown'}`}\n >\n <Globe />\n <span\n className={styles.networkIndicator}\n style={{ background: networkColor[cluster?.id || 'solana:mainnet'] }}\n />\n </Button>\n )}\n />\n )}\n </div>\n </div>\n\n {showSolBalance && (\n <div className={styles.balanceSection}>\n <div\n className={styles.balanceHeader}\n >\n <span className={styles.balanceLabel}>Balance</span>\n <button\n onClick={() => fetchSolBalance()}\n disabled={isFetchingBalance}\n title=\"Refresh balance\"\n className={styles.refreshButton}\n data-loading={isFetchingBalance}\n >\n <RefreshCw\n className={styles.refreshIcon}\n />\n </button>\n </div>\n <div className={styles.balanceValue}>\n {isFetchingBalance ? (\n <div className={styles.balanceLoading} />\n ) : solBalance !== null ? (\n `${solBalance.toFixed(4)} SOL`\n ) : (\n '-- SOL'\n )}\n </div>\n </div>\n )}\n\n <DisconnectElement\n render={({ disconnect, disconnecting }) => (\n <Button\n variant=\"default\"\n className={styles.disconnectButton}\n onClick={disconnect}\n disabled={disconnecting}\n >\n <LogOut className={styles.disconnectIcon} />\n {disconnecting ? 'Disconnecting...' : 'Disconnect'}\n </Button>\n )}\n />\n </motion.div>\n )\n }\n\n //network switch view\n if (view === 'network') {\n console.count('Network view rendered');\n return (\n <motion.div\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={{ duration: 0.2 }}\n className={clsx(styles.WalletDropdown, CN_ConnectButton)}\n data-theme={theme}\n >\n {/* Header */}\n <div className={styles.NetworkHeader}>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"icon\"\n onClick={() => setView('wallet')}\n title={`Network: Back to Wallet`}\n className={styles.backButton}\n >\n <ChevronLeft />\n </Button>\n <span>Network Settings</span>\n </div>\n\n {/* Network Options */}\n <ClusterElement\n render={({ cluster, clusters, setCluster }) => {\n const currentClusterId = (cluster as { id?: string })?.id || 'solana:mainnet';\n return (\n <div className={styles.networkOptions}>\n {clusters.map((network, index) => {\n const isSelected = currentClusterId === network.id;\n return (\n <div\n key={network.id}\n role=\"button\"\n tabIndex={0}\n onClick={() => setCluster(network.id)}\n onKeyDown={e => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n setCluster(network.id);\n }\n }}\n className={styles.networkButton}\n >\n <div className={styles.networkName}>\n <span\n className={styles.networkColor}\n style={{ background: networkColor[network.id] }}\n />\n <span className={styles.networkLabel}>{network.label}</span>\n </div>\n <div className={styles.checkMark} data-selected={isSelected}>\n {isSelected && <Check />}\n </div>\n </div>\n );\n })}\n </div>\n );\n }}\n />\n </motion.div>\n )\n }\n}\n\nexport default WalletDropdown","import { useState } from 'react';\nimport styles from './Avatar.module.css';\nimport { Wallet } from 'lucide-react';\ninterface AvatarProps {\n height?: number | string;\n width?: number | string;\n src?: string;\n alt?: string;\n theme?: 'light' | 'dark';\n}\nexport function Avatar({\n height,\n width,\n src,\n alt,\n theme = 'light',\n}: AvatarProps) {\n const [hasError, setHasError] = useState(false);\n return (\n <div className={styles.avatar} data-theme={theme}>\n {src && !hasError ? (\n <img\n height={height}\n width={width}\n src={src}\n alt={alt || \"Avatar\"}\n onError={() => setHasError(true)}\n />\n ) : (\n <div className={styles.fallback} style={{ height, width }}>\n <Wallet />\n </div>\n )}\n </div>\n );\n}\n","import { Avatar } from './Avatar';\n\nexport default Avatar;","'use client';\n\nimport * as React from 'react';\nimport { Button as BaseButton } from '@base-ui/react/button';\nimport styles from './Button.module.css';\n\n// 1. Define Types\ntype ButtonVariant = 'default' | 'destructive' | 'outline' | 'secondary' | 'ghost' | 'link';\ntype ButtonSize = 'default' | 'sm' | 'lg' | 'icon';\n\nexport interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n variant?: ButtonVariant;\n size?: ButtonSize;\n}\n\n// 2. The Component\nconst Button = React.forwardRef<HTMLButtonElement, ButtonProps>(\n ({ className, variant = 'default', size = 'default', ...props }, ref) => {\n return (\n <BaseButton\n ref={ref}\n\n className={`${styles.button} ${className || ''}`}\n data-variant={variant}\n data-size={size}\n\n {...props}\n />\n );\n }\n);\nButton.displayName = 'Button';\n\nexport default Button;","import Button from \"./Button\";\n\nexport default Button;"],"mappings":"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","names":["WalletDropdown_default","import_react","styles","import_lucide_react","import_jsx_runtime","BaseButton","styles","Button_default","import_jsx_runtime","styles","Button_default","WalletDropdown_default"]}
1
+ {"version":3,"sources":["../src/components/WalletDropdown/index.ts","../src/components/WalletDropdown/WalletDropdown.tsx","../src/components/shared/Avatar/Avatar.tsx","../src/components/shared/Avatar/index.ts","../src/components/shared/Button/Button.tsx","../src/components/shared/Button/index.ts","../src/utils/fetchBalance.tsx"],"sourcesContent":["import WalletDropdown from './WalletDropdown';\n\n\nexport default WalletDropdown","import { useEffect, useRef, useState } from 'react'\nimport { isAddress } from '@solana/kit';\nimport styles from './WalletDropdown.module.css'\nimport { motion } from 'motion/react';\nimport Avatar from '@shared/Avatar';\nimport { Check, ChevronLeft, Copy, Globe, LogOut, RefreshCw } from 'lucide-react';\nimport Button from '@shared/Button';\nimport { ClusterElement, DisconnectElement, useConnector, useConnectorClient } from '@solana/connector';\nimport { clsx } from 'clsx';\nimport { getSolBalance, getTokenBalance } from 'src/utils/fetchBalance';\n\n/**\n * Props for the WalletDropdown component.\n */\ninterface WalletDropdownProps {\n /** * Custom CSS class for the dropdown menu container. \n * If not passed, the component uses default absolute positioning.\n */\n CN_DropdownMenu?: string;\n\n /** * Visual theme for the dropdown items. \n * @default 'light'\n */\n theme?: 'light' | 'dark';\n\n /** * Enables the option to switch between Solana clusters. \n * @default true\n */\n allowNetworkSwitch?: boolean;\n\n /** * Displays the user's SOL balance inside the dropdown header. \n * @default true\n */\n showSolBalance?: boolean;\n\n /** * Configuration to display a specific SPL token balance. \n * If not provided, this defaults to false (hidden).\n */\n showDefaultToken?: {\n address: string;\n symbol: string;\n } | undefined;\n}\n\ntype DropdownView = 'wallet' | 'network';\n\nconst networkColor: Record<string, string> = {\n 'solana:mainnet': '#00c950',\n 'solana:devnet': '#2b7fff',\n 'solana:testnet': '#f0b100',\n 'solana:localnet': '#ff3b3b',\n};\n\nexport function WalletDropdown(props: WalletDropdownProps) {\n const client = useConnectorClient();\n\n const { CN_DropdownMenu,\n theme = 'light',\n allowNetworkSwitch = true,\n showSolBalance = true,\n showDefaultToken\n } = props\n\n const [view, setView] = useState<DropdownView>('wallet');\n const [copied, setCopied] = useState(false);\n\n const { account, connector } = useConnector();\n const fetchingSolBalance = useRef(false);\n const [isFetchingBalance, setIsFetchingBalance] = useState(false);\n\n const fetchingDefaultToken = useRef(false);\n const [isFetchingDefaultTokenBalance, setIsFetchingDefaultTokenBalance] = useState(false);\n\n const [solBalance, setSolBalance] = useState<number | null>(null);\n const [defaultTokenBalance, setDefaultTokenBalance] = useState<number | null>(null);\n\n const selectedAccount = account || '';\n const walletName = connector?.name || 'Unknown Wallet';\n const walletIcon = connector?.icon || undefined;\n const shortAddress = `${selectedAccount.slice(0, 4)}...${selectedAccount.slice(-4)}`;\n\n async function handleCopy() {\n try {\n await navigator.clipboard.writeText(selectedAccount);\n setCopied(true);\n setTimeout(() => setCopied(false), 2000);\n } catch (error) {\n setCopied(false);\n console.error('Failed to copy to clipboard:', error);\n }\n }\n\n async function fetchSolBalance() {\n if (!client || fetchingSolBalance.current) return;\n setIsFetchingBalance(true);\n fetchingSolBalance.current = true;\n\n const solBalance = await getSolBalance(client, selectedAccount);\n setSolBalance(solBalance);\n\n setIsFetchingBalance(false);\n fetchingSolBalance.current = false;\n }\n\n async function fetchDefaultTokenBalance() {\n if (!showDefaultToken || !showDefaultToken.address || !client || fetchingDefaultToken.current) return;\n\n const isValidAddress = isAddress(showDefaultToken.address);\n if (!isValidAddress) {\n console.error('Invalid default token address:', showDefaultToken);\n return\n }\n\n setIsFetchingDefaultTokenBalance(true);\n fetchingDefaultToken.current = true;\n\n const tokenBalance = await getTokenBalance(client, selectedAccount, showDefaultToken.address);\n setDefaultTokenBalance(tokenBalance);\n\n setIsFetchingDefaultTokenBalance(false);\n fetchingDefaultToken.current = false;\n }\n\n useEffect(() => {\n if (showSolBalance && selectedAccount && client) {\n fetchSolBalance();\n }\n if (showDefaultToken && selectedAccount && client) {\n fetchDefaultTokenBalance();\n }\n }, [selectedAccount, client, showSolBalance, showDefaultToken]);\n\n if (view === 'wallet') {\n return (\n <motion.div\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={{ duration: 0.2 }}\n className={clsx(styles.WalletDropdown, CN_DropdownMenu)}\n data-theme={theme}\n >\n {/* Header with Avatar and Address */}\n <div className={styles.Header}>\n <div className={styles.addressAndAvatar}>\n <Avatar\n width={48}\n height={48}\n src={walletIcon}\n alt={walletName}\n />\n <div className={styles.address}>\n <span className={styles.shortAddress}>{shortAddress}</span>\n <span className={styles.walletName}>{walletName}</span>\n </div>\n </div>\n\n <div className={styles.actions}>\n <Button\n type=\"button\"\n onClick={handleCopy}\n variant=\"outline\"\n size=\"icon\"\n className=\"rounded-full\"\n title={copied ? 'Copied!' : 'Copy address'}\n >\n {copied ?\n <Check className={styles.checkIcon} /> :\n <Copy />\n }\n </Button>\n\n {/* Network Selector Globe Button */}\n {allowNetworkSwitch && (\n <ClusterElement\n render={({ cluster }) => (\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"icon\"\n onClick={() => setView('network')}\n title={`Network: ${cluster?.label || 'Unknown'}`}\n >\n <Globe />\n <span\n className={styles.networkIndicator}\n style={{ background: networkColor[cluster?.id || 'solana:mainnet'] }}\n />\n </Button>\n )}\n />\n )}\n </div>\n </div>\n\n {showSolBalance && (\n <div className={styles.balanceSection}>\n <div\n className={styles.balanceHeader}\n >\n <span className={styles.balanceLabel}>Balance</span>\n <button\n onClick={() => fetchSolBalance()}\n disabled={isFetchingBalance}\n title=\"Refresh balance\"\n className={styles.refreshButton}\n data-loading={isFetchingBalance}\n >\n <RefreshCw\n className={styles.refreshIcon}\n />\n </button>\n </div>\n <div className={styles.balanceValue} title={String(solBalance) || \"0\"}>\n {isFetchingBalance ? (\n <div className={styles.balanceLoading} />\n ) : solBalance !== null ? (\n `${solBalance.toFixed(4)} SOL`\n ) : (\n '-- SOL'\n )}\n </div>\n </div>\n )}\n\n {showDefaultToken && (\n <div className={styles.balanceSection}>\n <div\n className={styles.balanceHeader}\n >\n <span className={styles.balanceLabel}>Balance</span>\n <button\n onClick={() => fetchDefaultTokenBalance()}\n disabled={isFetchingDefaultTokenBalance}\n title=\"Refresh balance\"\n className={styles.refreshButton}\n data-loading={isFetchingDefaultTokenBalance}\n >\n <RefreshCw\n className={styles.refreshIcon}\n />\n </button>\n </div>\n <div className={styles.balanceValue} title={String(defaultTokenBalance) || \"0\"}>\n {isFetchingDefaultTokenBalance ? (\n <div className={styles.balanceLoading} />\n ) : defaultTokenBalance !== null ? (\n `${defaultTokenBalance.toFixed(4)} ${showDefaultToken?.symbol || ''}`\n ) : (\n `-- ${showDefaultToken?.symbol || ''}`\n )}\n </div>\n </div>\n )}\n\n <DisconnectElement\n render={({ disconnect, disconnecting }) => (\n <Button\n variant=\"default\"\n className={styles.disconnectButton}\n onClick={disconnect}\n disabled={disconnecting}\n >\n <LogOut className={styles.disconnectIcon} />\n {disconnecting ? 'Disconnecting...' : 'Disconnect'}\n </Button>\n )}\n />\n </motion.div>\n )\n }\n\n //network switch view\n if (view === 'network') {\n return (\n <motion.div\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={{ duration: 0.2 }}\n className={clsx(styles.WalletDropdown, CN_DropdownMenu)}\n data-theme={theme}\n >\n {/* Header */}\n <div className={styles.NetworkHeader}>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"icon\"\n onClick={() => setView('wallet')}\n title={`Network: Back to Wallet`}\n className={styles.backButton}\n >\n <ChevronLeft />\n </Button>\n <span>Network Settings</span>\n </div>\n\n {/* Network Options */}\n <ClusterElement\n render={({ cluster, clusters, setCluster }) => {\n const currentClusterId = (cluster as { id?: string })?.id || 'solana:mainnet';\n return (\n <div className={styles.networkOptions}>\n {clusters.map((network) => {\n const isSelected = currentClusterId === network.id;\n return (\n <div\n key={network.id}\n role=\"button\"\n tabIndex={0}\n onClick={() => setCluster(network.id)}\n onKeyDown={e => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n setCluster(network.id);\n }\n }}\n className={styles.networkButton}\n >\n <div className={styles.networkName}>\n <span\n className={styles.networkColor}\n style={{ background: networkColor[network.id] }}\n />\n <span className={styles.networkLabel}>{network.label}</span>\n </div>\n <div className={styles.checkMark} data-selected={isSelected}>\n {isSelected && <Check />}\n </div>\n </div>\n );\n })}\n </div>\n );\n }}\n />\n </motion.div>\n )\n }\n}\n\nexport default WalletDropdown","import { useState } from 'react';\nimport styles from './Avatar.module.css';\nimport { Wallet } from 'lucide-react';\ninterface AvatarProps {\n height?: number | string;\n width?: number | string;\n src?: string;\n alt?: string;\n theme?: 'light' | 'dark';\n}\nexport function Avatar({\n height,\n width,\n src,\n alt,\n theme = 'light',\n}: AvatarProps) {\n const [hasError, setHasError] = useState(false);\n return (\n <div className={styles.avatar} data-theme={theme}>\n {src && !hasError ? (\n <img\n height={height}\n width={width}\n src={src}\n alt={alt || \"Avatar\"}\n onError={() => setHasError(true)}\n />\n ) : (\n <div className={styles.fallback} style={{ height, width }}>\n <Wallet />\n </div>\n )}\n </div>\n );\n}\n","import { Avatar } from './Avatar';\n\nexport default Avatar;","'use client';\n\nimport { forwardRef } from 'react';\nimport { Button as BaseButton } from '@base-ui/react/button';\nimport styles from './Button.module.css';\n\n// 1. Define Types\ntype ButtonVariant = 'default' | 'destructive' | 'outline' | 'secondary' | 'ghost' | 'link';\ntype ButtonSize = 'default' | 'sm' | 'lg' | 'icon';\n\nexport interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n variant?: ButtonVariant;\n size?: ButtonSize;\n}\n\n// 2. The Component\nconst Button = forwardRef<HTMLButtonElement, ButtonProps>(\n ({ className, variant = 'default', size = 'default', ...props }, ref) => {\n return (\n <BaseButton\n ref={ref}\n\n className={`${styles.button} ${className || ''}`}\n data-variant={variant}\n data-size={size}\n\n {...props}\n />\n );\n }\n);\nButton.displayName = 'Button';\n\nexport default Button;","import Button from \"./Button\";\n\nexport default Button;","import { findAssociatedTokenPda } from \"@solana-program/token\";\nimport { address, ConnectorClient, lamportsToSol } from \"@solana/connector\";\nimport { createSolanaRpc } from \"@solana/kit\";\n\nexport async function getSolBalance(\n client: ConnectorClient,\n pubkey: string\n): Promise<number> {\n let balance = 0;\n try {\n const rpcUrl = client.getRpcUrl();\n if (!rpcUrl) {\n console.error(\"RPC URL is not available from the ConnectorClient.\");\n return 0;\n }\n const pubkeyAddress = address(pubkey);\n const rpc = createSolanaRpc(rpcUrl);\n const balanceResponse = await rpc.getBalance(pubkeyAddress).send();\n balance = lamportsToSol(balanceResponse.value);\n } catch (error) {\n console.error(\"Error fetching SOL balance:\", error);\n } finally {\n return balance;\n }\n}\n\nexport async function getTokenBalance(\n client: ConnectorClient,\n pubkey: string,\n mintAddress: string\n): Promise<number> {\n let balance = 0;\n try {\n const rpcUrl = client.getRpcUrl();\n if (!rpcUrl) {\n console.error(\"RPC URL is not available from the ConnectorClient.\");\n return 0;\n }\n const pubkeyAddress = address(pubkey);\n const mintPubkey = address(mintAddress);\n const rpc = createSolanaRpc(rpcUrl);\n\n const mintInfo = await rpc.getAccountInfo(\n mintPubkey,\n { encoding: \"base64\" }\n ).send();\n\n const ownerProgram = mintInfo.value?.owner;\n if (!ownerProgram) {\n throw new Error('Failed to fetch mint account info');\n }\n const tokenProgram = address(ownerProgram)\n const [tokenPDA] = await findAssociatedTokenPda({\n mint: mintPubkey,\n owner: pubkeyAddress,\n tokenProgram: tokenProgram\n });\n const tokenBalance = await rpc.getTokenAccountBalance(tokenPDA).send();\n if (tokenBalance.value) {\n balance = parseFloat(tokenBalance.value.uiAmountString);\n }\n } catch (error) {\n // console.error(\"Error fetching token balance:\", error);\n } finally {\n return balance;\n }\n}"],"mappings":"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","names":["WalletDropdown_default","import_react","import_kit","styles","import_lucide_react","import_react","import_jsx_runtime","BaseButton","styles","Button_default","import_connector","import_jsx_runtime","solBalance","styles","Button_default","WalletDropdown_default"]}
@@ -2,7 +2,8 @@
2
2
 
3
3
  // src/components/WalletDropdown/WalletDropdown.tsx
4
4
  import { useEffect, useRef, useState as useState2 } from "react";
5
- import styles3 from "./WalletDropdown.module-A6KHWKGK.module.css";
5
+ import { isAddress } from "@solana/kit";
6
+ import styles3 from "./WalletDropdown.module-DOK7CUOQ.module.css";
6
7
  import { motion } from "motion/react";
7
8
 
8
9
  // src/components/shared/Avatar/Avatar.tsx
@@ -37,11 +38,11 @@ var Avatar_default = Avatar;
37
38
  import { Check, ChevronLeft, Copy, Globe, LogOut, RefreshCw } from "lucide-react";
38
39
 
39
40
  // src/components/shared/Button/Button.tsx
40
- import * as React from "react";
41
+ import { forwardRef } from "react";
41
42
  import { Button as BaseButton } from "@base-ui/react/button";
42
- import styles2 from "./Button.module-HQNNX6IB.module.css";
43
+ import styles2 from "./Button.module-QCTUNBHA.module.css";
43
44
  import { jsx as jsx2 } from "react/jsx-runtime";
44
- var Button = React.forwardRef(
45
+ var Button = forwardRef(
45
46
  ({ className, variant = "default", size = "default", ...props }, ref) => {
46
47
  return /* @__PURE__ */ jsx2(
47
48
  BaseButton,
@@ -62,9 +63,68 @@ var Button_default = Button;
62
63
  var Button_default2 = Button_default;
63
64
 
64
65
  // src/components/WalletDropdown/WalletDropdown.tsx
65
- import { address, ClusterElement, DisconnectElement, lamportsToSol, useConnectorClient } from "@solana/connector";
66
- import { createSolanaRpc } from "@solana/kit";
66
+ import { ClusterElement, DisconnectElement, useConnector, useConnectorClient } from "@solana/connector";
67
67
  import { clsx } from "clsx";
68
+
69
+ // src/utils/fetchBalance.tsx
70
+ import { findAssociatedTokenPda } from "@solana-program/token";
71
+ import { address, lamportsToSol } from "@solana/connector";
72
+ import { createSolanaRpc } from "@solana/kit";
73
+ async function getSolBalance(client, pubkey) {
74
+ let balance = 0;
75
+ try {
76
+ const rpcUrl = client.getRpcUrl();
77
+ if (!rpcUrl) {
78
+ console.error("RPC URL is not available from the ConnectorClient.");
79
+ return 0;
80
+ }
81
+ const pubkeyAddress = address(pubkey);
82
+ const rpc = createSolanaRpc(rpcUrl);
83
+ const balanceResponse = await rpc.getBalance(pubkeyAddress).send();
84
+ balance = lamportsToSol(balanceResponse.value);
85
+ } catch (error) {
86
+ console.error("Error fetching SOL balance:", error);
87
+ } finally {
88
+ return balance;
89
+ }
90
+ }
91
+ async function getTokenBalance(client, pubkey, mintAddress) {
92
+ var _a;
93
+ let balance = 0;
94
+ try {
95
+ const rpcUrl = client.getRpcUrl();
96
+ if (!rpcUrl) {
97
+ console.error("RPC URL is not available from the ConnectorClient.");
98
+ return 0;
99
+ }
100
+ const pubkeyAddress = address(pubkey);
101
+ const mintPubkey = address(mintAddress);
102
+ const rpc = createSolanaRpc(rpcUrl);
103
+ const mintInfo = await rpc.getAccountInfo(
104
+ mintPubkey,
105
+ { encoding: "base64" }
106
+ ).send();
107
+ const ownerProgram = (_a = mintInfo.value) == null ? void 0 : _a.owner;
108
+ if (!ownerProgram) {
109
+ throw new Error("Failed to fetch mint account info");
110
+ }
111
+ const tokenProgram = address(ownerProgram);
112
+ const [tokenPDA] = await findAssociatedTokenPda({
113
+ mint: mintPubkey,
114
+ owner: pubkeyAddress,
115
+ tokenProgram
116
+ });
117
+ const tokenBalance = await rpc.getTokenAccountBalance(tokenPDA).send();
118
+ if (tokenBalance.value) {
119
+ balance = parseFloat(tokenBalance.value.uiAmountString);
120
+ }
121
+ } catch (error) {
122
+ } finally {
123
+ return balance;
124
+ }
125
+ }
126
+
127
+ // src/components/WalletDropdown/WalletDropdown.tsx
68
128
  import { jsx as jsx3, jsxs } from "react/jsx-runtime";
69
129
  var networkColor = {
70
130
  "solana:mainnet": "#00c950",
@@ -74,12 +134,25 @@ var networkColor = {
74
134
  };
75
135
  function WalletDropdown(props) {
76
136
  const client = useConnectorClient();
77
- const { CN_ConnectButton, selectedAccount, walletIcon, walletName, theme, allowNetworkSwitch, showSolBalance } = props;
137
+ const {
138
+ CN_DropdownMenu,
139
+ theme = "light",
140
+ allowNetworkSwitch = true,
141
+ showSolBalance = true,
142
+ showDefaultToken
143
+ } = props;
78
144
  const [view, setView] = useState2("wallet");
79
145
  const [copied, setCopied] = useState2(false);
80
- const fetching = useRef(false);
146
+ const { account, connector } = useConnector();
147
+ const fetchingSolBalance = useRef(false);
81
148
  const [isFetchingBalance, setIsFetchingBalance] = useState2(false);
149
+ const fetchingDefaultToken = useRef(false);
150
+ const [isFetchingDefaultTokenBalance, setIsFetchingDefaultTokenBalance] = useState2(false);
82
151
  const [solBalance, setSolBalance] = useState2(null);
152
+ const [defaultTokenBalance, setDefaultTokenBalance] = useState2(null);
153
+ const selectedAccount = account || "";
154
+ const walletName = (connector == null ? void 0 : connector.name) || "Unknown Wallet";
155
+ const walletIcon = (connector == null ? void 0 : connector.icon) || void 0;
83
156
  const shortAddress = `${selectedAccount.slice(0, 4)}...${selectedAccount.slice(-4)}`;
84
157
  async function handleCopy() {
85
158
  try {
@@ -92,29 +165,36 @@ function WalletDropdown(props) {
92
165
  }
93
166
  }
94
167
  async function fetchSolBalance() {
95
- if (!client || fetching.current) return;
168
+ if (!client || fetchingSolBalance.current) return;
96
169
  setIsFetchingBalance(true);
97
- fetching.current = true;
98
- try {
99
- const rpcUrl = client.getRpcUrl();
100
- const pubkey = address(selectedAccount);
101
- if (!rpcUrl) throw new Error("No RPC endpoint configured");
102
- const rpc = createSolanaRpc(rpcUrl);
103
- const solLamports = (await rpc.getBalance(pubkey).send()).value || 0;
104
- const sol = lamportsToSol(solLamports);
105
- setSolBalance(sol);
106
- } catch (error) {
107
- setSolBalance(0);
108
- } finally {
109
- setIsFetchingBalance(false);
110
- fetching.current = false;
170
+ fetchingSolBalance.current = true;
171
+ const solBalance2 = await getSolBalance(client, selectedAccount);
172
+ setSolBalance(solBalance2);
173
+ setIsFetchingBalance(false);
174
+ fetchingSolBalance.current = false;
175
+ }
176
+ async function fetchDefaultTokenBalance() {
177
+ if (!showDefaultToken || !showDefaultToken.address || !client || fetchingDefaultToken.current) return;
178
+ const isValidAddress = isAddress(showDefaultToken.address);
179
+ if (!isValidAddress) {
180
+ console.error("Invalid default token address:", showDefaultToken);
181
+ return;
111
182
  }
183
+ setIsFetchingDefaultTokenBalance(true);
184
+ fetchingDefaultToken.current = true;
185
+ const tokenBalance = await getTokenBalance(client, selectedAccount, showDefaultToken.address);
186
+ setDefaultTokenBalance(tokenBalance);
187
+ setIsFetchingDefaultTokenBalance(false);
188
+ fetchingDefaultToken.current = false;
112
189
  }
113
190
  useEffect(() => {
114
191
  if (showSolBalance && selectedAccount && client) {
115
192
  fetchSolBalance();
116
193
  }
117
- }, [selectedAccount, client, showSolBalance]);
194
+ if (showDefaultToken && selectedAccount && client) {
195
+ fetchDefaultTokenBalance();
196
+ }
197
+ }, [selectedAccount, client, showSolBalance, showDefaultToken]);
118
198
  if (view === "wallet") {
119
199
  return /* @__PURE__ */ jsxs(
120
200
  motion.div,
@@ -123,7 +203,7 @@ function WalletDropdown(props) {
123
203
  animate: { opacity: 1 },
124
204
  exit: { opacity: 0 },
125
205
  transition: { duration: 0.2 },
126
- className: clsx(styles3.WalletDropdown, CN_ConnectButton),
206
+ className: clsx(styles3.WalletDropdown, CN_DropdownMenu),
127
207
  "data-theme": theme,
128
208
  children: [
129
209
  /* @__PURE__ */ jsxs("div", { className: styles3.Header, children: [
@@ -208,7 +288,35 @@ function WalletDropdown(props) {
208
288
  ]
209
289
  }
210
290
  ),
211
- /* @__PURE__ */ jsx3("div", { className: styles3.balanceValue, children: isFetchingBalance ? /* @__PURE__ */ jsx3("div", { className: styles3.balanceLoading }) : solBalance !== null ? `${solBalance.toFixed(4)} SOL` : "-- SOL" })
291
+ /* @__PURE__ */ jsx3("div", { className: styles3.balanceValue, title: String(solBalance) || "0", children: isFetchingBalance ? /* @__PURE__ */ jsx3("div", { className: styles3.balanceLoading }) : solBalance !== null ? `${solBalance.toFixed(4)} SOL` : "-- SOL" })
292
+ ] }),
293
+ showDefaultToken && /* @__PURE__ */ jsxs("div", { className: styles3.balanceSection, children: [
294
+ /* @__PURE__ */ jsxs(
295
+ "div",
296
+ {
297
+ className: styles3.balanceHeader,
298
+ children: [
299
+ /* @__PURE__ */ jsx3("span", { className: styles3.balanceLabel, children: "Balance" }),
300
+ /* @__PURE__ */ jsx3(
301
+ "button",
302
+ {
303
+ onClick: () => fetchDefaultTokenBalance(),
304
+ disabled: isFetchingDefaultTokenBalance,
305
+ title: "Refresh balance",
306
+ className: styles3.refreshButton,
307
+ "data-loading": isFetchingDefaultTokenBalance,
308
+ children: /* @__PURE__ */ jsx3(
309
+ RefreshCw,
310
+ {
311
+ className: styles3.refreshIcon
312
+ }
313
+ )
314
+ }
315
+ )
316
+ ]
317
+ }
318
+ ),
319
+ /* @__PURE__ */ jsx3("div", { className: styles3.balanceValue, title: String(defaultTokenBalance) || "0", children: isFetchingDefaultTokenBalance ? /* @__PURE__ */ jsx3("div", { className: styles3.balanceLoading }) : defaultTokenBalance !== null ? `${defaultTokenBalance.toFixed(4)} ${(showDefaultToken == null ? void 0 : showDefaultToken.symbol) || ""}` : `-- ${(showDefaultToken == null ? void 0 : showDefaultToken.symbol) || ""}` })
212
320
  ] }),
213
321
  /* @__PURE__ */ jsx3(
214
322
  DisconnectElement,
@@ -233,7 +341,6 @@ function WalletDropdown(props) {
233
341
  );
234
342
  }
235
343
  if (view === "network") {
236
- console.count("Network view rendered");
237
344
  return /* @__PURE__ */ jsxs(
238
345
  motion.div,
239
346
  {
@@ -241,7 +348,7 @@ function WalletDropdown(props) {
241
348
  animate: { opacity: 1 },
242
349
  exit: { opacity: 0 },
243
350
  transition: { duration: 0.2 },
244
- className: clsx(styles3.WalletDropdown, CN_ConnectButton),
351
+ className: clsx(styles3.WalletDropdown, CN_DropdownMenu),
245
352
  "data-theme": theme,
246
353
  children: [
247
354
  /* @__PURE__ */ jsxs("div", { className: styles3.NetworkHeader, children: [
@@ -264,7 +371,7 @@ function WalletDropdown(props) {
264
371
  {
265
372
  render: ({ cluster, clusters, setCluster }) => {
266
373
  const currentClusterId = (cluster == null ? void 0 : cluster.id) || "solana:mainnet";
267
- return /* @__PURE__ */ jsx3("div", { className: styles3.networkOptions, children: clusters.map((network, index) => {
374
+ return /* @__PURE__ */ jsx3("div", { className: styles3.networkOptions, children: clusters.map((network) => {
268
375
  const isSelected = currentClusterId === network.id;
269
376
  return /* @__PURE__ */ jsxs(
270
377
  "div",