@b3dotfun/sdk 0.0.40-alpha.3 → 0.0.40-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.
- package/dist/cjs/global-account/react/components/B3Provider/B3Provider.d.ts +2 -2
- package/dist/cjs/global-account/react/components/B3Provider/B3Provider.js +14 -7
- package/dist/cjs/global-account/react/components/LinkAccount/LinkAccount.js +63 -3
- package/dist/cjs/global-account/react/components/ManageAccount/ManageAccount.js +35 -2
- package/dist/esm/global-account/react/components/B3Provider/B3Provider.d.ts +2 -2
- package/dist/esm/global-account/react/components/B3Provider/B3Provider.js +13 -5
- package/dist/esm/global-account/react/components/LinkAccount/LinkAccount.js +65 -5
- package/dist/esm/global-account/react/components/ManageAccount/ManageAccount.js +35 -2
- package/dist/styles/index.css +1 -1
- package/dist/types/global-account/react/components/B3Provider/B3Provider.d.ts +2 -2
- package/package.json +1 -1
- package/src/global-account/react/components/B3Provider/B3Provider.tsx +15 -4
- package/src/global-account/react/components/LinkAccount/LinkAccount.tsx +106 -32
- package/src/global-account/react/components/ManageAccount/ManageAccount.tsx +60 -5
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import app from "@b3dotfun/sdk/global-account/app";
|
|
2
2
|
import { ecosystemWalletId } from "@b3dotfun/sdk/shared/constants";
|
|
3
|
+
import { thirdwebB3Mainnet } from "@b3dotfun/sdk/shared/constants/chains/b3Chain";
|
|
3
4
|
import { client } from "@b3dotfun/sdk/shared/utils/thirdweb";
|
|
4
|
-
import { Loader2, Mail, Phone } from "lucide-react";
|
|
5
|
+
import { Loader2, Mail, Phone, WalletIcon } from "lucide-react";
|
|
5
6
|
import { useCallback, useEffect, useState } from "react";
|
|
6
7
|
import { toast } from "sonner";
|
|
7
8
|
import { useLinkProfile, useProfiles } from "thirdweb/react";
|
|
8
|
-
import { preAuthenticate } from "thirdweb/wallets";
|
|
9
|
+
import { createWallet, preAuthenticate, WalletId } from "thirdweb/wallets";
|
|
10
|
+
import { WalletRow } from "../..";
|
|
9
11
|
import { LinkAccountModalProps, useModalStore } from "../../stores/useModalStore";
|
|
10
12
|
import { getProfileDisplayInfo } from "../../utils/profileDisplay";
|
|
11
13
|
import { useB3 } from "../B3Provider/useB3";
|
|
@@ -17,13 +19,14 @@ import { XIcon } from "../icons/XIcon";
|
|
|
17
19
|
import { Button } from "../ui/button";
|
|
18
20
|
type OTPStrategy = "email" | "phone";
|
|
19
21
|
type SocialStrategy = "google" | "x" | "discord" | "apple" | "farcaster";
|
|
20
|
-
type Strategy = OTPStrategy | SocialStrategy;
|
|
22
|
+
type Strategy = OTPStrategy | SocialStrategy | "wallet";
|
|
21
23
|
|
|
22
24
|
interface AuthMethod {
|
|
23
25
|
id: Strategy;
|
|
24
26
|
label: string;
|
|
25
27
|
enabled: boolean;
|
|
26
28
|
icon: React.ReactNode;
|
|
29
|
+
walletType?: WalletId;
|
|
27
30
|
}
|
|
28
31
|
|
|
29
32
|
const AUTH_METHODS: AuthMethod[] = [
|
|
@@ -41,6 +44,39 @@ const AUTH_METHODS: AuthMethod[] = [
|
|
|
41
44
|
},
|
|
42
45
|
];
|
|
43
46
|
|
|
47
|
+
const WALLET_METHODS: AuthMethod[] = [
|
|
48
|
+
{
|
|
49
|
+
id: "wallet",
|
|
50
|
+
label: "Wallet",
|
|
51
|
+
enabled: true,
|
|
52
|
+
icon: <WalletIcon className="size-6" />,
|
|
53
|
+
walletType: "com.coinbase.wallet",
|
|
54
|
+
},
|
|
55
|
+
{ id: "wallet", label: "Wallet", enabled: true, icon: <WalletIcon className="size-6" />, walletType: "io.metamask" },
|
|
56
|
+
{
|
|
57
|
+
id: "wallet",
|
|
58
|
+
label: "Wallet",
|
|
59
|
+
enabled: true,
|
|
60
|
+
icon: <WalletIcon className="size-6" />,
|
|
61
|
+
walletType: "me.rainbow",
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
id: "wallet",
|
|
65
|
+
label: "Wallet",
|
|
66
|
+
enabled: true,
|
|
67
|
+
icon: <WalletIcon className="size-6" />,
|
|
68
|
+
walletType: "app.phantom",
|
|
69
|
+
},
|
|
70
|
+
{ id: "wallet", label: "Wallet", enabled: true, icon: <WalletIcon className="size-6" />, walletType: "io.rabby" },
|
|
71
|
+
{
|
|
72
|
+
id: "wallet",
|
|
73
|
+
label: "Wallet",
|
|
74
|
+
enabled: true,
|
|
75
|
+
icon: <WalletIcon className="size-6" />,
|
|
76
|
+
walletType: "walletConnect",
|
|
77
|
+
},
|
|
78
|
+
];
|
|
79
|
+
|
|
44
80
|
export function LinkAccount({
|
|
45
81
|
onSuccess: onSuccessCallback,
|
|
46
82
|
onError,
|
|
@@ -60,7 +96,7 @@ export function LinkAccount({
|
|
|
60
96
|
|
|
61
97
|
// Get connected auth methods
|
|
62
98
|
const connectedAuthMethods = profilesRaw
|
|
63
|
-
.filter((profile: any) => !["custom_auth_endpoint"
|
|
99
|
+
.filter((profile: any) => !["custom_auth_endpoint"].includes(profile.type))
|
|
64
100
|
.map((profile: any) => profile.type as Strategy);
|
|
65
101
|
|
|
66
102
|
// Filter available auth methods
|
|
@@ -69,7 +105,7 @@ export function LinkAccount({
|
|
|
69
105
|
);
|
|
70
106
|
|
|
71
107
|
const profiles = profilesRaw
|
|
72
|
-
.filter((profile: any) => !["custom_auth_endpoint"
|
|
108
|
+
.filter((profile: any) => !["custom_auth_endpoint"].includes(profile.type))
|
|
73
109
|
.map((profile: any) => ({
|
|
74
110
|
...getProfileDisplayInfo(profile),
|
|
75
111
|
originalProfile: profile,
|
|
@@ -211,6 +247,29 @@ export function LinkAccount({
|
|
|
211
247
|
}
|
|
212
248
|
};
|
|
213
249
|
|
|
250
|
+
const handleLinkWallet = async (walletType: WalletId) => {
|
|
251
|
+
setLinkingState(true, "wallet");
|
|
252
|
+
console.log("selectedMethod", walletType);
|
|
253
|
+
try {
|
|
254
|
+
if (!walletType) {
|
|
255
|
+
throw new Error("Wallet type not found");
|
|
256
|
+
}
|
|
257
|
+
await linkProfile(
|
|
258
|
+
{
|
|
259
|
+
client,
|
|
260
|
+
strategy: "wallet",
|
|
261
|
+
wallet: createWallet(walletType),
|
|
262
|
+
chain: thirdwebB3Mainnet,
|
|
263
|
+
},
|
|
264
|
+
mutationOptions,
|
|
265
|
+
);
|
|
266
|
+
} catch (error) {
|
|
267
|
+
console.error("Error linking account:", error);
|
|
268
|
+
setError(error instanceof Error ? error.message : "Failed to link account");
|
|
269
|
+
onError?.(error as Error);
|
|
270
|
+
}
|
|
271
|
+
};
|
|
272
|
+
|
|
214
273
|
const handleSocialLink = async (strategy: SocialStrategy) => {
|
|
215
274
|
try {
|
|
216
275
|
console.log("handleSocialLink", strategy);
|
|
@@ -338,6 +397,20 @@ export function LinkAccount({
|
|
|
338
397
|
)}
|
|
339
398
|
</Button>
|
|
340
399
|
))}
|
|
400
|
+
{WALLET_METHODS.map(method => {
|
|
401
|
+
if (!method.walletType) {
|
|
402
|
+
return null;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
return (
|
|
406
|
+
<WalletRow
|
|
407
|
+
key={method.walletType}
|
|
408
|
+
walletId={method.walletType as WalletId}
|
|
409
|
+
onClick={() => handleLinkWallet(method.walletType as WalletId)}
|
|
410
|
+
isLoading={isLinking}
|
|
411
|
+
/>
|
|
412
|
+
);
|
|
413
|
+
})}
|
|
341
414
|
{availableAuthMethods.length === 0 && (
|
|
342
415
|
<div className="text-b3-foreground-muted py-8 text-center">
|
|
343
416
|
All available authentication methods have been connected
|
|
@@ -379,38 +452,39 @@ export function LinkAccount({
|
|
|
379
452
|
|
|
380
453
|
{error && <div className="text-b3-negative font-neue-montreal-medium py-2 text-sm">{error}</div>}
|
|
381
454
|
|
|
382
|
-
{
|
|
383
|
-
|
|
384
|
-
<div className="space-y-
|
|
385
|
-
<
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
455
|
+
{(selectedMethod === "email" || selectedMethod === "phone") &&
|
|
456
|
+
(otpSent ? (
|
|
457
|
+
<div className="space-y-4">
|
|
458
|
+
<div className="space-y-2">
|
|
459
|
+
<label className="text-b3-grey font-neue-montreal-medium text-sm">Verification Code</label>
|
|
460
|
+
<input
|
|
461
|
+
type="text"
|
|
462
|
+
placeholder="Enter verification code"
|
|
463
|
+
className="bg-b3-line text-b3-grey font-neue-montreal-medium focus:ring-b3-primary-blue/20 w-full rounded-xl p-4 focus:outline-none focus:ring-2"
|
|
464
|
+
value={otp}
|
|
465
|
+
onChange={e => setOtp(e.target.value)}
|
|
466
|
+
/>
|
|
467
|
+
</div>
|
|
468
|
+
<Button
|
|
469
|
+
className="bg-b3-primary-blue hover:bg-b3-primary-blue/90 font-neue-montreal-semibold h-12 w-full text-white"
|
|
470
|
+
onClick={handleLinkAccount}
|
|
471
|
+
>
|
|
472
|
+
Link Account
|
|
473
|
+
</Button>
|
|
393
474
|
</div>
|
|
475
|
+
) : (
|
|
394
476
|
<Button
|
|
395
477
|
className="bg-b3-primary-blue hover:bg-b3-primary-blue/90 font-neue-montreal-semibold h-12 w-full text-white"
|
|
396
|
-
onClick={
|
|
478
|
+
onClick={handleSendOTP}
|
|
479
|
+
disabled={(!email && !phone) || (isLinking && linkingMethod === selectedMethod)}
|
|
397
480
|
>
|
|
398
|
-
|
|
481
|
+
{isLinking && linkingMethod === selectedMethod ? (
|
|
482
|
+
<Loader2 className="animate-spin" />
|
|
483
|
+
) : (
|
|
484
|
+
"Send Verification Code"
|
|
485
|
+
)}
|
|
399
486
|
</Button>
|
|
400
|
-
|
|
401
|
-
) : (
|
|
402
|
-
<Button
|
|
403
|
-
className="bg-b3-primary-blue hover:bg-b3-primary-blue/90 font-neue-montreal-semibold h-12 w-full text-white"
|
|
404
|
-
onClick={handleSendOTP}
|
|
405
|
-
disabled={(!email && !phone) || (isLinking && linkingMethod === selectedMethod)}
|
|
406
|
-
>
|
|
407
|
-
{isLinking && linkingMethod === selectedMethod ? (
|
|
408
|
-
<Loader2 className="animate-spin" />
|
|
409
|
-
) : (
|
|
410
|
-
"Send Verification Code"
|
|
411
|
-
)}
|
|
412
|
-
</Button>
|
|
413
|
-
)}
|
|
487
|
+
))}
|
|
414
488
|
</div>
|
|
415
489
|
)}
|
|
416
490
|
</div>
|
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
import { SignOutIcon } from "@b3dotfun/sdk/global-account/react/components/icons/SignOutIcon";
|
|
19
19
|
import { formatNumber } from "@b3dotfun/sdk/shared/utils/formatNumber";
|
|
20
20
|
import { client } from "@b3dotfun/sdk/shared/utils/thirdweb";
|
|
21
|
+
import { truncateAddress } from "@b3dotfun/sdk/shared/utils/truncateAddress";
|
|
21
22
|
import { BarChart3, Coins, Copy, Image, LinkIcon, Loader2, Pencil, Settings, UnlinkIcon } from "lucide-react";
|
|
22
23
|
import { useRef, useState } from "react";
|
|
23
24
|
import { toast } from "sonner";
|
|
@@ -29,6 +30,24 @@ import { getProfileDisplayInfo } from "../../utils/profileDisplay";
|
|
|
29
30
|
import { AccountAssets } from "../AccountAssets/AccountAssets";
|
|
30
31
|
import { ContentTokens } from "./ContentTokens";
|
|
31
32
|
|
|
33
|
+
// Helper function to check if a string is a wallet address and format it
|
|
34
|
+
const formatProfileTitle = (title: string): { displayTitle: string; isAddress: boolean } => {
|
|
35
|
+
// Check if title looks like an Ethereum address (0x followed by 40 hex characters)
|
|
36
|
+
const isEthereumAddress = /^0x[a-fA-F0-9]{40}$/.test(title);
|
|
37
|
+
|
|
38
|
+
if (isEthereumAddress) {
|
|
39
|
+
return {
|
|
40
|
+
displayTitle: truncateAddress(title),
|
|
41
|
+
isAddress: true,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return {
|
|
46
|
+
displayTitle: title,
|
|
47
|
+
isAddress: false,
|
|
48
|
+
};
|
|
49
|
+
};
|
|
50
|
+
|
|
32
51
|
import { Referrals, Users } from "@b3dotfun/b3-api";
|
|
33
52
|
import { BalanceContent } from "./BalanceContent";
|
|
34
53
|
|
|
@@ -200,7 +219,7 @@ export function ManageAccount({
|
|
|
200
219
|
};
|
|
201
220
|
|
|
202
221
|
const profiles = profilesRaw
|
|
203
|
-
.filter((profile: any) => !["custom_auth_endpoint"
|
|
222
|
+
.filter((profile: any) => !["custom_auth_endpoint"].includes(profile.type))
|
|
204
223
|
.map((profile: any) => ({
|
|
205
224
|
...getProfileDisplayInfo(profile),
|
|
206
225
|
originalProfile: profile,
|
|
@@ -236,6 +255,8 @@ export function ManageAccount({
|
|
|
236
255
|
});
|
|
237
256
|
};
|
|
238
257
|
|
|
258
|
+
console.log("@@profiles", profiles);
|
|
259
|
+
|
|
239
260
|
return (
|
|
240
261
|
<div className="linked-accounts-settings space-y-8">
|
|
241
262
|
{/* Linked Accounts Section */}
|
|
@@ -269,7 +290,7 @@ export function ManageAccount({
|
|
|
269
290
|
{profiles.map(profile => (
|
|
270
291
|
<div
|
|
271
292
|
key={profile.title}
|
|
272
|
-
className="linked-account-item bg-b3-line flex items-center justify-between rounded-xl p-4"
|
|
293
|
+
className="linked-account-item bg-b3-line group flex items-center justify-between rounded-xl p-4"
|
|
273
294
|
>
|
|
274
295
|
<div className="linked-account-info flex items-center gap-3">
|
|
275
296
|
{profile.imageUrl ? (
|
|
@@ -287,9 +308,43 @@ export function ManageAccount({
|
|
|
287
308
|
)}
|
|
288
309
|
<div className="linked-account-details">
|
|
289
310
|
<div className="linked-account-title-row flex items-center gap-2">
|
|
290
|
-
|
|
291
|
-
{profile.title
|
|
292
|
-
|
|
311
|
+
{(() => {
|
|
312
|
+
const { displayTitle, isAddress } = formatProfileTitle(profile.title);
|
|
313
|
+
|
|
314
|
+
const handleCopyAddress = async (e: React.MouseEvent) => {
|
|
315
|
+
e.stopPropagation();
|
|
316
|
+
try {
|
|
317
|
+
await navigator.clipboard.writeText(profile.title);
|
|
318
|
+
toast.success("Address copied to clipboard!");
|
|
319
|
+
} catch (error) {
|
|
320
|
+
toast.error("Failed to copy address");
|
|
321
|
+
}
|
|
322
|
+
};
|
|
323
|
+
|
|
324
|
+
return (
|
|
325
|
+
<div className="flex items-center gap-1">
|
|
326
|
+
<span
|
|
327
|
+
className={`linked-account-title text-b3-grey font-neue-montreal-semibold ${
|
|
328
|
+
isAddress
|
|
329
|
+
? "font-mono text-sm" // Use monospace font for addresses
|
|
330
|
+
: "break-words" // Use break-words for emails/names (better than break-all)
|
|
331
|
+
}`}
|
|
332
|
+
title={isAddress ? profile.title : undefined} // Show full address on hover
|
|
333
|
+
>
|
|
334
|
+
{displayTitle}
|
|
335
|
+
</span>
|
|
336
|
+
{isAddress && (
|
|
337
|
+
<button
|
|
338
|
+
onClick={handleCopyAddress}
|
|
339
|
+
className="linked-account-copy-button ml-1 rounded p-1 opacity-0 transition-opacity hover:bg-gray-100 group-hover:opacity-100"
|
|
340
|
+
title="Copy full address"
|
|
341
|
+
>
|
|
342
|
+
<Copy size={12} className="text-gray-500 hover:text-gray-700" />
|
|
343
|
+
</button>
|
|
344
|
+
)}
|
|
345
|
+
</div>
|
|
346
|
+
);
|
|
347
|
+
})()}
|
|
293
348
|
<span className="linked-account-type text-b3-foreground-muted font-neue-montreal-medium bg-b3-primary-wash rounded px-2 py-0.5 text-xs">
|
|
294
349
|
{profile.type.toUpperCase()}
|
|
295
350
|
</span>
|