@b3dotfun/sdk 0.0.30-alpha.9 → 0.0.31-alpha.0

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 (71) hide show
  1. package/dist/cjs/anyspend/react/components/AnyspendDepositHype.d.ts +4 -0
  2. package/dist/cjs/anyspend/react/components/AnyspendDepositHype.js +6 -1
  3. package/dist/cjs/anyspend/react/components/common/CryptoReceiveSection.d.ts +3 -1
  4. package/dist/cjs/anyspend/react/components/common/CryptoReceiveSection.js +2 -2
  5. package/dist/cjs/anyspend/react/components/common/PanelOnramp.d.ts +3 -1
  6. package/dist/cjs/anyspend/react/components/common/PanelOnramp.js +3 -3
  7. package/dist/cjs/global-account/react/components/ManageAccount/BalanceContent.d.ts +6 -0
  8. package/dist/cjs/global-account/react/components/ManageAccount/BalanceContent.js +94 -0
  9. package/dist/cjs/global-account/react/components/ManageAccount/ContentTokens.js +5 -0
  10. package/dist/cjs/global-account/react/components/ManageAccount/ManageAccount.js +3 -40
  11. package/dist/cjs/global-account/react/components/ManageAccount/TokenBalanceRow.d.ts +10 -0
  12. package/dist/cjs/global-account/react/components/ManageAccount/TokenBalanceRow.js +8 -0
  13. package/dist/cjs/global-account/react/components/TokenIcon.d.ts +11 -0
  14. package/dist/cjs/global-account/react/components/TokenIcon.js +43 -0
  15. package/dist/cjs/global-account/react/components/ui/accordion.d.ts +7 -0
  16. package/dist/cjs/global-account/react/components/ui/accordion.js +53 -0
  17. package/dist/cjs/global-account/react/components/ui/dialog.js +1 -1
  18. package/dist/cjs/global-account/react/hooks/useUnifiedChainSwitchAndExecute.js +2 -0
  19. package/dist/cjs/global-account/utils/analytics.d.ts +6 -0
  20. package/dist/cjs/global-account/utils/analytics.js +1 -0
  21. package/dist/esm/anyspend/react/components/AnyspendDepositHype.d.ts +4 -0
  22. package/dist/esm/anyspend/react/components/AnyspendDepositHype.js +5 -1
  23. package/dist/esm/anyspend/react/components/common/CryptoReceiveSection.d.ts +3 -1
  24. package/dist/esm/anyspend/react/components/common/CryptoReceiveSection.js +2 -2
  25. package/dist/esm/anyspend/react/components/common/PanelOnramp.d.ts +3 -1
  26. package/dist/esm/anyspend/react/components/common/PanelOnramp.js +4 -4
  27. package/dist/esm/global-account/react/components/ManageAccount/BalanceContent.d.ts +6 -0
  28. package/dist/esm/global-account/react/components/ManageAccount/BalanceContent.js +88 -0
  29. package/dist/esm/global-account/react/components/ManageAccount/ContentTokens.js +2 -0
  30. package/dist/esm/global-account/react/components/ManageAccount/ManageAccount.js +6 -40
  31. package/dist/esm/global-account/react/components/ManageAccount/TokenBalanceRow.d.ts +10 -0
  32. package/dist/esm/global-account/react/components/ManageAccount/TokenBalanceRow.js +5 -0
  33. package/dist/esm/global-account/react/components/TokenIcon.d.ts +11 -0
  34. package/dist/esm/global-account/react/components/TokenIcon.js +37 -0
  35. package/dist/esm/global-account/react/components/ui/accordion.d.ts +7 -0
  36. package/dist/esm/global-account/react/components/ui/accordion.js +14 -0
  37. package/dist/esm/global-account/react/components/ui/dialog.js +1 -1
  38. package/dist/esm/global-account/react/hooks/useUnifiedChainSwitchAndExecute.js +2 -0
  39. package/dist/esm/global-account/utils/analytics.d.ts +6 -0
  40. package/dist/esm/global-account/utils/analytics.js +1 -0
  41. package/dist/styles/index.css +1 -1
  42. package/dist/types/anyspend/react/components/AnyspendDepositHype.d.ts +4 -0
  43. package/dist/types/anyspend/react/components/common/CryptoReceiveSection.d.ts +3 -1
  44. package/dist/types/anyspend/react/components/common/PanelOnramp.d.ts +3 -1
  45. package/dist/types/global-account/react/components/ManageAccount/BalanceContent.d.ts +6 -0
  46. package/dist/types/global-account/react/components/ManageAccount/TokenBalanceRow.d.ts +10 -0
  47. package/dist/types/global-account/react/components/TokenIcon.d.ts +11 -0
  48. package/dist/types/global-account/react/components/ui/accordion.d.ts +7 -0
  49. package/dist/types/global-account/utils/analytics.d.ts +6 -0
  50. package/package.json +10 -18
  51. package/src/anyspend/react/components/AnySpendStakeB3.tsx +1 -1
  52. package/src/anyspend/react/components/AnyspendDepositHype.tsx +9 -0
  53. package/src/anyspend/react/components/AnyspendSignatureMint.tsx +4 -4
  54. package/src/anyspend/react/components/common/CryptoReceiveSection.tsx +12 -3
  55. package/src/anyspend/react/components/common/PanelOnramp.tsx +8 -4
  56. package/src/global-account/react/components/ManageAccount/BalanceContent.tsx +228 -0
  57. package/src/global-account/react/components/ManageAccount/ContentTokens.tsx +3 -0
  58. package/src/global-account/react/components/ManageAccount/ManageAccount.tsx +7 -285
  59. package/src/global-account/react/components/ManageAccount/TokenBalanceRow.tsx +46 -0
  60. package/src/global-account/react/components/TokenIcon.tsx +87 -0
  61. package/src/global-account/react/components/ui/accordion.tsx +53 -0
  62. package/src/global-account/react/components/ui/dialog.tsx +1 -1
  63. package/src/global-account/react/hooks/useB3BalanceFromAddresses.ts +1 -1
  64. package/src/global-account/react/hooks/useUnifiedChainSwitchAndExecute.ts +3 -0
  65. package/src/global-account/utils/analytics.ts +10 -0
  66. package/dist/cjs/index.d.ts +0 -0
  67. package/dist/cjs/index.js +0 -2
  68. package/dist/esm/index.d.ts +0 -0
  69. package/dist/esm/index.js +0 -2
  70. package/dist/types/index.d.ts +0 -0
  71. package/src/index.ts +0 -1
@@ -1,6 +1,5 @@
1
1
  import {
2
2
  Button,
3
- CopyToClipboard,
4
3
  ManageAccountModalProps,
5
4
  TabsContentPrimitive,
6
5
  TabsListPrimitive,
@@ -9,30 +8,26 @@ import {
9
8
  TWSignerWithMetadata,
10
9
  useAccountAssets,
11
10
  useAuthentication,
12
- useB3BalanceFromAddresses,
13
11
  useGetAllTWSigners,
14
12
  useModalStore,
15
- useNativeBalance,
16
- useProfile,
17
13
  useRemoveSessionKey,
18
14
  } from "@b3dotfun/sdk/global-account/react";
19
- import { BankIcon } from "@b3dotfun/sdk/global-account/react/components/icons/BankIcon";
20
15
  import { SignOutIcon } from "@b3dotfun/sdk/global-account/react/components/icons/SignOutIcon";
21
- import { SwapIcon } from "@b3dotfun/sdk/global-account/react/components/icons/SwapIcon";
22
- import { formatUsername } from "@b3dotfun/sdk/shared/utils";
23
16
  import { formatNumber } from "@b3dotfun/sdk/shared/utils/formatNumber";
24
17
 
25
18
  import { client } from "@b3dotfun/sdk/shared/utils/thirdweb";
26
- import { BarChart3, Coins, Image, LinkIcon, Loader2, Pencil, Settings, Triangle, UnlinkIcon } from "lucide-react";
19
+ import { BarChart3, Coins, Image, LinkIcon, Loader2, Settings, UnlinkIcon } from "lucide-react";
27
20
  import { useState } from "react";
28
21
  import { Chain } from "thirdweb";
29
22
  import { useActiveAccount, useProfiles, useUnlinkProfile } from "thirdweb/react";
30
23
  import { formatUnits } from "viem";
31
- import useFirstEOA from "../../hooks/useFirstEOA";
24
+
32
25
  import { getProfileDisplayInfo } from "../../utils/profileDisplay";
33
26
  import { AccountAssets } from "../AccountAssets/AccountAssets";
34
27
  import { ContentTokens } from "./ContentTokens";
35
28
 
29
+ import { BalanceContent } from "./BalanceContent";
30
+
36
31
  type TabValue = "overview" | "tokens" | "nfts" | "apps" | "settings";
37
32
 
38
33
  interface ManageAccountProps {
@@ -45,11 +40,6 @@ interface ManageAccountProps {
45
40
  containerClassName?: string;
46
41
  }
47
42
 
48
- function centerTruncate(str: string, length = 4) {
49
- if (str.length <= length * 2) return str;
50
- return `${str.slice(0, length)}...${str.slice(-length)}`;
51
- }
52
-
53
43
  export function ManageAccount({
54
44
  onLogout,
55
45
  onSwap: _onSwap,
@@ -60,20 +50,12 @@ export function ManageAccount({
60
50
  const [revokingSignerId, setRevokingSignerId] = useState<string | null>(null);
61
51
  const account = useActiveAccount();
62
52
  const { data: nfts, isLoading } = useAccountAssets(account?.address);
63
- const { data: b3Balance } = useB3BalanceFromAddresses(account?.address);
64
- const { data: nativeBalance } = useNativeBalance(account?.address);
65
- const { address: eoaAddress } = useFirstEOA();
66
- const { data: profile } = useProfile({
67
- address: eoaAddress || account?.address,
68
- fresh: true,
69
- });
70
- const { data: eoaNativeBalance } = useNativeBalance(eoaAddress);
71
- const { data: eoaB3Balance } = useB3BalanceFromAddresses(eoaAddress);
53
+
72
54
  const { data: signers, refetch: refetchSigners } = useGetAllTWSigners({
73
55
  chain,
74
56
  accountAddress: account?.address,
75
57
  });
76
- const { setB3ModalOpen, setB3ModalContentType, contentType } = useModalStore();
58
+ const { setB3ModalOpen, contentType } = useModalStore();
77
59
  const { activeTab = "overview", setActiveTab } = contentType as ManageAccountModalProps;
78
60
  const { logout } = useAuthentication(partnerId);
79
61
  const [logoutLoading, setLogoutLoading] = useState(false);
@@ -104,266 +86,6 @@ export function ManageAccount({
104
86
  setLogoutLoading(false);
105
87
  };
106
88
 
107
- const BalanceContent = () => {
108
- const { info: eoaInfo } = useFirstEOA();
109
-
110
- return (
111
- <div className="flex flex-col gap-6">
112
- {/* Profile Section */}
113
- <div className="flex items-center justify-between">
114
- <div className="flex items-center gap-4">
115
- <div className="relative">
116
- {profile?.avatar ? (
117
- <img src={profile?.avatar} alt="Profile" className="size-24 rounded-full" />
118
- ) : (
119
- <div className="bg-b3-primary-wash size-24 rounded-full" />
120
- )}
121
- <div className="bg-b3-grey border-b3-background absolute -bottom-1 -right-1 flex size-8 items-center justify-center rounded-full border-4">
122
- <Pencil size={16} className="text-b3-background" />
123
- </div>
124
- </div>
125
- <div>
126
- <h2 className="text-b3-grey text-xl font-semibold">
127
- {profile?.displayName || formatUsername(profile?.name || "")}
128
- </h2>
129
- <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">
130
- <span className="text-b3-foreground-muted font-mono text-xs">
131
- {centerTruncate(account?.address || "", 6)}
132
- </span>
133
- <CopyToClipboard text={account?.address || ""} />
134
- </div>
135
- </div>
136
- </div>
137
- </div>
138
-
139
- {/* Quick Actions */}
140
- <div className="grid grid-cols-2 gap-3">
141
- <Button
142
- className="manage-account-deposit bg-b3-primary-wash hover:bg-b3-primary-wash/70 h-[84px] w-full flex-col items-start gap-2 rounded-2xl"
143
- onClick={() => {
144
- setB3ModalOpen(true);
145
- setB3ModalContentType({
146
- type: "anySpend",
147
- defaultActiveTab: "fiat",
148
- showBackButton: true,
149
- });
150
- }}
151
- >
152
- <BankIcon size={24} className="text-b3-primary-blue shrink-0" />
153
- <div className="text-b3-grey font-neue-montreal-semibold">Deposit</div>
154
- </Button>
155
- <Button
156
- className="manage-account-swap bg-b3-primary-wash hover:bg-b3-primary-wash/70 flex h-[84px] w-full flex-col items-start gap-2 rounded-2xl"
157
- onClick={() => {
158
- setB3ModalOpen(true);
159
- setB3ModalContentType({
160
- type: "anySpend",
161
- showBackButton: true,
162
- });
163
- }}
164
- >
165
- <SwapIcon size={24} className="text-b3-primary-blue" />
166
- <div className="text-b3-grey font-neue-montreal-semibold">Swap</div>
167
- </Button>
168
- </div>
169
-
170
- {/* Balance Section */}
171
- <div className="space-y-4">
172
- <h3 className="text-b3-grey font-neue-montreal-semibold">Balance</h3>
173
-
174
- {/* B3 Balance */}
175
- <div className="flex items-center justify-between">
176
- <div className="flex items-center gap-3">
177
- <div className="flex h-10 w-10 items-center justify-center rounded-full">
178
- <img src="https://cdn.b3.fun/b3-coin-3d.png" alt="B3" className="size-10" />
179
- </div>
180
- <div>
181
- <div className="flex items-center gap-2">
182
- <span className="text-b3-grey font-neue-montreal-semibold">B3</span>
183
- </div>
184
- <div className="text-b3-foreground-muted font-neue-montreal-medium text-sm">
185
- {b3Balance?.formattedTotal || "0.00"} B3
186
- </div>
187
- </div>
188
- </div>
189
- <div className="text-right">
190
- <div className="text-b3-grey font-neue-montreal-semibold">
191
- ${b3Balance?.balanceUsdFormatted || "0.00"}
192
- </div>
193
- <div className="flex items-center gap-1">
194
- {b3Balance?.priceChange24h !== null && b3Balance?.priceChange24h !== undefined ? (
195
- <>
196
- <Triangle
197
- className={`size-3 ${b3Balance.priceChange24h >= 0 ? "text-b3-positive fill-b3-positive" : "text-b3-negative fill-b3-negative rotate-180"}`}
198
- />
199
- <span
200
- className={`font-neue-montreal-medium text-sm ${b3Balance.priceChange24h >= 0 ? "text-b3-positive" : "text-b3-negative"}`}
201
- >
202
- {b3Balance.priceChange24h >= 0 ? "+" : ""}
203
- {b3Balance.priceChange24h.toFixed(2)}%
204
- </span>
205
- </>
206
- ) : (
207
- <span className="text-b3-foreground-muted font-neue-montreal-medium text-sm">--</span>
208
- )}
209
- </div>
210
- </div>
211
- </div>
212
-
213
- {/* ETH Balance */}
214
- <div className="flex items-center justify-between">
215
- <div className="flex items-center gap-3">
216
- <div className="flex h-10 w-10 items-center justify-center rounded-full">
217
- <img src="https://cdn.b3.fun/ethereum.svg" alt="ETH" className="size-10" />
218
- </div>
219
- <div>
220
- <div className="flex items-center gap-2">
221
- <span className="text-b3-grey font-neue-montreal-semibold">Ethereum</span>
222
- </div>
223
- <div className="text-b3-foreground-muted font-neue-montreal-medium text-sm">
224
- {nativeBalance?.formattedTotal || "0.00"} ETH
225
- </div>
226
- </div>
227
- </div>
228
- <div className="text-right">
229
- <div className="text-b3-grey font-neue-montreal-semibold">
230
- ${nativeBalance?.formattedTotalUsd || "0.00"}
231
- </div>
232
- <div className="flex items-center gap-2">
233
- {nativeBalance?.priceChange24h !== null && nativeBalance?.priceChange24h !== undefined ? (
234
- <>
235
- <Triangle
236
- className={`size-3 ${nativeBalance.priceChange24h >= 0 ? "text-b3-positive fill-b3-positive" : "text-b3-negative fill-b3-negative rotate-180"}`}
237
- />
238
- <span
239
- className={`font-neue-montreal-medium text-sm ${nativeBalance.priceChange24h >= 0 ? "text-b3-positive" : "text-b3-negative"}`}
240
- >
241
- {nativeBalance.priceChange24h >= 0 ? "+" : ""}
242
- {nativeBalance.priceChange24h.toFixed(2)}%
243
- </span>
244
- </>
245
- ) : (
246
- <span className="text-b3-foreground-muted font-neue-montreal-medium text-sm">--</span>
247
- )}
248
- </div>
249
- </div>
250
- </div>
251
- </div>
252
-
253
- {/* EOA Account Balance Section - matching global overview styling */}
254
- {eoaAddress && (
255
- <div className="space-y-4">
256
- <div className="flex items-center gap-3">
257
- <h3 className="text-b3-grey font-neue-montreal-semibold">Connected {eoaInfo?.data?.name || "Wallet"}</h3>
258
- <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">
259
- <span className="text-b3-foreground-muted font-mono text-xs">{centerTruncate(eoaAddress, 6)}</span>
260
- <CopyToClipboard text={eoaAddress} />
261
- </div>
262
- </div>
263
-
264
- {/* EOA B3 Balance */}
265
- <div className="flex items-center justify-between">
266
- <div className="flex items-center gap-3">
267
- <div className="flex h-10 w-10 items-center justify-center rounded-full">
268
- <img src="https://cdn.b3.fun/b3-coin-3d.png" alt="B3" className="size-10" />
269
- </div>
270
- <div>
271
- <div className="flex items-center gap-2">
272
- <span className="text-b3-grey font-neue-montreal-semibold">B3</span>
273
- </div>
274
- <div className="text-b3-foreground-muted font-neue-montreal-medium text-sm">
275
- {eoaB3Balance?.formattedTotal || "0.00"} B3
276
- </div>
277
- </div>
278
- </div>
279
- <div className="text-right">
280
- <div className="text-b3-grey font-neue-montreal-semibold">
281
- ${eoaB3Balance?.balanceUsdFormatted || "0.00"}
282
- </div>
283
- <div className="flex items-center gap-1">
284
- {eoaB3Balance?.priceChange24h !== null && eoaB3Balance?.priceChange24h !== undefined ? (
285
- <>
286
- <Triangle
287
- className={`size-3 ${eoaB3Balance.priceChange24h >= 0 ? "text-b3-positive fill-b3-positive" : "text-b3-negative fill-b3-negative rotate-180"}`}
288
- />
289
- <span
290
- className={`font-neue-montreal-medium text-sm ${eoaB3Balance.priceChange24h >= 0 ? "text-b3-positive" : "text-b3-negative"}`}
291
- >
292
- {eoaB3Balance.priceChange24h >= 0 ? "+" : ""}
293
- {eoaB3Balance.priceChange24h.toFixed(2)}%
294
- </span>
295
- </>
296
- ) : (
297
- <span className="text-b3-foreground-muted font-neue-montreal-medium text-sm">--</span>
298
- )}
299
- </div>
300
- </div>
301
- </div>
302
-
303
- {/* EOA ETH Balance */}
304
- <div className="flex items-center justify-between">
305
- <div className="flex items-center gap-3">
306
- <div className="flex h-10 w-10 items-center justify-center rounded-full">
307
- <img src="https://cdn.b3.fun/ethereum.svg" alt="ETH" className="size-10" />
308
- </div>
309
- <div>
310
- <div className="flex items-center gap-2">
311
- <span className="text-b3-grey font-neue-montreal-semibold">Ethereum</span>
312
- </div>
313
- <div className="text-b3-foreground-muted font-neue-montreal-medium text-sm">
314
- {eoaNativeBalance?.formattedTotal || "0.00"} ETH
315
- </div>
316
- </div>
317
- </div>
318
- <div className="text-right">
319
- <div className="text-b3-grey font-neue-montreal-semibold">
320
- ${eoaNativeBalance?.formattedTotalUsd || "0.00"}
321
- </div>
322
- <div className="flex items-center gap-2">
323
- {eoaNativeBalance?.priceChange24h !== null && eoaNativeBalance?.priceChange24h !== undefined ? (
324
- <>
325
- <Triangle
326
- className={`size-3 ${eoaNativeBalance.priceChange24h >= 0 ? "text-b3-positive fill-b3-positive" : "text-b3-negative fill-b3-negative rotate-180"}`}
327
- />
328
- <span
329
- className={`font-neue-montreal-medium text-sm ${eoaNativeBalance.priceChange24h >= 0 ? "text-b3-positive" : "text-b3-negative"}`}
330
- >
331
- {eoaNativeBalance.priceChange24h >= 0 ? "+" : ""}
332
- {eoaNativeBalance.priceChange24h.toFixed(2)}%
333
- </span>
334
- </>
335
- ) : (
336
- <span className="text-b3-foreground-muted font-neue-montreal-medium text-sm">--</span>
337
- )}
338
- </div>
339
- </div>
340
- </div>
341
- </div>
342
- )}
343
-
344
- {/* Global Account Info */}
345
- <div className="border-b3-line flex items-center justify-between rounded-2xl border p-4">
346
- <div className="">
347
- <div className="flex items-center gap-2">
348
- <img src="https://cdn.b3.fun/b3_logo.svg" alt="B3" className="h-4" />
349
- <h3 className="font-neue-montreal-semibold text-b3-grey">Global Account</h3>
350
- </div>
351
-
352
- <p className="text-b3-foreground-muted font-neue-montreal-medium mt-2 text-sm">
353
- Your universal account for all B3 apps
354
- </p>
355
- </div>
356
- <button
357
- 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"
358
- onClick={onLogoutEnhanced}
359
- >
360
- {logoutLoading ? <Loader2 className="animate-spin" /> : <SignOutIcon size={16} className="text-b3-grey" />}
361
- </button>
362
- </div>
363
- </div>
364
- );
365
- };
366
-
367
89
  const AppsContent = () => (
368
90
  <div className="space-y-4">
369
91
  {signers?.map((signer: TWSignerWithMetadata) => (
@@ -629,7 +351,7 @@ export function ManageAccount({
629
351
  </div>
630
352
 
631
353
  <TabsContentPrimitive value="overview" className="px-4 pb-4 pt-2">
632
- <BalanceContent />
354
+ <BalanceContent onLogout={onLogout} partnerId={partnerId} />
633
355
  </TabsContentPrimitive>
634
356
 
635
357
  <TabsContentPrimitive value="tokens" className="px-4 pb-4 pt-2">
@@ -0,0 +1,46 @@
1
+ import { Triangle } from "lucide-react";
2
+ import { ReactNode } from "react";
3
+
4
+ interface TokenBalanceRowProps {
5
+ icon: ReactNode;
6
+ name: string;
7
+ balance: string;
8
+ usdValue: string;
9
+ priceChange?: number | null;
10
+ }
11
+
12
+ export function TokenBalanceRow({ icon, name, balance, usdValue, priceChange }: TokenBalanceRowProps) {
13
+ return (
14
+ <div className="flex items-center justify-between">
15
+ <div className="flex items-center gap-3">
16
+ <div className="flex h-10 w-10 items-center justify-center rounded-full">{icon}</div>
17
+ <div>
18
+ <div className="flex items-center gap-2">
19
+ <span className="text-b3-grey font-neue-montreal-semibold">{name}</span>
20
+ </div>
21
+ <div className="text-b3-foreground-muted font-neue-montreal-medium text-sm">{balance}</div>
22
+ </div>
23
+ </div>
24
+ <div className="text-right">
25
+ <div className="text-b3-grey font-neue-montreal-semibold">${usdValue}</div>
26
+ <div className="flex items-center gap-1">
27
+ {priceChange !== null && priceChange !== undefined ? (
28
+ <>
29
+ <Triangle
30
+ className={`size-3 ${priceChange >= 0 ? "text-b3-positive fill-b3-positive" : "text-b3-negative fill-b3-negative rotate-180"}`}
31
+ />
32
+ <span
33
+ className={`font-neue-montreal-medium text-sm ${priceChange >= 0 ? "text-b3-positive" : "text-b3-negative"}`}
34
+ >
35
+ {priceChange >= 0 ? "+" : ""}
36
+ {priceChange.toFixed(2)}%
37
+ </span>
38
+ </>
39
+ ) : (
40
+ <span className="text-b3-foreground-muted font-neue-montreal-medium text-sm">--</span>
41
+ )}
42
+ </div>
43
+ </div>
44
+ </div>
45
+ );
46
+ }
@@ -0,0 +1,87 @@
1
+ import { cn } from "@b3dotfun/sdk/shared/utils";
2
+ import React, { useCallback, useMemo, useState } from "react";
3
+
4
+ interface TokenIconProps {
5
+ src: string;
6
+ alt: string;
7
+ className?: string;
8
+ size?: number;
9
+ }
10
+
11
+ // Create a global image cache to prevent re-loading
12
+ const imageCache = new Map<string, boolean>();
13
+
14
+ export const TokenIcon: React.FC<TokenIconProps> = ({ src, alt, className, size = 40 }) => {
15
+ const [isLoaded, setIsLoaded] = useState(() => imageCache.get(src) || false);
16
+ const [hasError, setHasError] = useState(false);
17
+
18
+ const handleLoad = useCallback(() => {
19
+ imageCache.set(src, true);
20
+ setIsLoaded(true);
21
+ }, [src]);
22
+
23
+ const handleError = useCallback(() => {
24
+ setHasError(true);
25
+ }, []);
26
+
27
+ // Memoize the image element to prevent unnecessary re-renders
28
+ const imageElement = useMemo(
29
+ () => (
30
+ <img
31
+ src={src}
32
+ alt={alt}
33
+ className={cn("transition-opacity duration-200", className, {
34
+ "opacity-100": isLoaded && !hasError,
35
+ "opacity-0": !isLoaded || hasError,
36
+ })}
37
+ onLoad={handleLoad}
38
+ onError={handleError}
39
+ loading="eager" // Load immediately since these are critical UI elements
40
+ decoding="async"
41
+ style={{
42
+ width: size,
43
+ height: size,
44
+ }}
45
+ />
46
+ ),
47
+ [src, alt, className, isLoaded, hasError, handleLoad, handleError, size],
48
+ );
49
+
50
+ // Show a placeholder while loading or if there's an error
51
+ const placeholder = useMemo(
52
+ () => (
53
+ <div
54
+ className={cn(
55
+ "bg-b3-primary-wash flex items-center justify-center rounded-full transition-opacity duration-200",
56
+ {
57
+ "opacity-0": isLoaded && !hasError,
58
+ "opacity-100": !isLoaded || hasError,
59
+ },
60
+ )}
61
+ style={{
62
+ width: size,
63
+ height: size,
64
+ }}
65
+ >
66
+ <span className="text-b3-grey font-neue-montreal-semibold text-xs">{alt.charAt(0).toUpperCase()}</span>
67
+ </div>
68
+ ),
69
+ [alt, isLoaded, hasError, size],
70
+ );
71
+
72
+ return (
73
+ <div className="relative inline-block" style={{ width: size, height: size }}>
74
+ {placeholder}
75
+ <div className="absolute inset-0">{imageElement}</div>
76
+ </div>
77
+ );
78
+ };
79
+
80
+ // Pre-defined token icons for common tokens
81
+ export const B3TokenIcon: React.FC<Omit<TokenIconProps, "src" | "alt">> = props => (
82
+ <TokenIcon src="https://cdn.b3.fun/b3-coin-3d.png" alt="B3" {...props} />
83
+ );
84
+
85
+ export const EthereumTokenIcon: React.FC<Omit<TokenIconProps, "src" | "alt">> = props => (
86
+ <TokenIcon src="https://cdn.b3.fun/ethereum.svg" alt="ETH" {...props} />
87
+ );
@@ -0,0 +1,53 @@
1
+ "use client";
2
+
3
+ import * as AccordionPrimitive from "@radix-ui/react-accordion";
4
+ import { ChevronDown } from "lucide-react";
5
+ import * as React from "react";
6
+
7
+ import { cn } from "@b3dotfun/sdk/shared/utils";
8
+
9
+ const Accordion = AccordionPrimitive.Root;
10
+
11
+ const AccordionItem = React.forwardRef<
12
+ React.ElementRef<typeof AccordionPrimitive.Item>,
13
+ React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Item>
14
+ >(({ className, ...props }, ref) => (
15
+ <AccordionPrimitive.Item ref={ref} className={cn("border-b3-line border-b", className)} {...props} />
16
+ ));
17
+ AccordionItem.displayName = "AccordionItem";
18
+
19
+ const AccordionTrigger = React.forwardRef<
20
+ React.ElementRef<typeof AccordionPrimitive.Trigger>,
21
+ React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Trigger>
22
+ >(({ className, children, ...props }, ref) => (
23
+ <AccordionPrimitive.Header className="flex">
24
+ <AccordionPrimitive.Trigger
25
+ ref={ref}
26
+ className={cn(
27
+ "flex flex-1 items-center justify-between py-4 font-medium transition-all hover:underline [&[data-state=open]>svg]:rotate-180",
28
+ className,
29
+ )}
30
+ {...props}
31
+ >
32
+ {children}
33
+ <ChevronDown className="h-4 w-4 shrink-0 transition-transform duration-200" />
34
+ </AccordionPrimitive.Trigger>
35
+ </AccordionPrimitive.Header>
36
+ ));
37
+ AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName;
38
+
39
+ const AccordionContent = React.forwardRef<
40
+ React.ElementRef<typeof AccordionPrimitive.Content>,
41
+ React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Content>
42
+ >(({ className, children, ...props }, ref) => (
43
+ <AccordionPrimitive.Content
44
+ ref={ref}
45
+ className="data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down overflow-hidden text-sm transition-all"
46
+ {...props}
47
+ >
48
+ <div className={cn("pb-4 pt-0", className)}>{children}</div>
49
+ </AccordionPrimitive.Content>
50
+ ));
51
+ AccordionContent.displayName = AccordionPrimitive.Content.displayName;
52
+
53
+ export { Accordion, AccordionContent, AccordionItem, AccordionTrigger };
@@ -64,7 +64,7 @@ const DialogContent: React.ForwardRefExoticComponent<DialogContentProps & React.
64
64
  {!hideCloseButton && (
65
65
  <DialogPrimitive.Close
66
66
  className={cn(
67
- "data-[state=open]:bg-b3-react-background data-[state=open]:text-b3-react-muted-foreground absolute right-2 top-2 rounded-sm opacity-70 transition-opacity hover:opacity-100 focus:outline-none disabled:pointer-events-none dark:data-[state=open]:bg-gray-800 dark:data-[state=open]:text-gray-400",
67
+ "modal-close-button data-[state=open]:bg-b3-react-background data-[state=open]:text-b3-react-muted-foreground absolute right-2 top-2 rounded-sm opacity-70 transition-opacity hover:opacity-100 focus:outline-none disabled:pointer-events-none dark:data-[state=open]:bg-gray-800 dark:data-[state=open]:text-gray-400",
68
68
  closeBtnClassName,
69
69
  )}
70
70
  >
@@ -46,7 +46,7 @@ async function fetchB3Balances(addresses: string[]): Promise<{
46
46
  const balances = await Promise.all(
47
47
  addresses.map(async address => {
48
48
  const balance = await client.readContract({
49
- address: B3_TOKEN.address,
49
+ address: B3_TOKEN.address as `0x${string}`,
50
50
  abi,
51
51
  functionName: "balanceOf",
52
52
  args: [address as `0x${string}`],
@@ -9,6 +9,7 @@ import { prepareTransaction, sendTransaction as twSendTransaction } from "thirdw
9
9
  import { useSwitchChain, useWalletClient } from "wagmi";
10
10
  import { useB3 } from "../components";
11
11
  import { useAccountWallet } from "./useAccountWallet";
12
+ import { isAddress } from "viem";
12
13
 
13
14
  export interface UnifiedTransactionParams {
14
15
  to: string;
@@ -56,6 +57,8 @@ export function useUnifiedChainSwitchAndExecute() {
56
57
  throw new Error(`Chain ${targetChainId} is not supported`);
57
58
  }
58
59
 
60
+ invariant(isAddress(params.to), "params.to is not a valid address");
61
+
59
62
  const hash = await walletClient.sendTransaction({
60
63
  account: signer,
61
64
  chain: targetChain,
@@ -1,3 +1,13 @@
1
+ // Global window interface augmentations for B3 SDK
2
+
3
+ declare global {
4
+ interface Window {
5
+ // Google Analytics 4
6
+ gtag: (...args: any[]) => void;
7
+ dataLayer: any[];
8
+ }
9
+ }
10
+
1
11
  const GA4_MEASUREMENT_ID = "G-VER9DKJH87";
2
12
 
3
13
  /**
File without changes
package/dist/cjs/index.js DELETED
@@ -1,2 +0,0 @@
1
- "use strict";
2
- //
File without changes
package/dist/esm/index.js DELETED
@@ -1,2 +0,0 @@
1
- "use strict";
2
- //
File without changes
package/src/index.ts DELETED
@@ -1 +0,0 @@
1
- //