@b3dotfun/sdk 0.0.20-alpha.0 → 0.0.20-alpha.2

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.
@@ -24,13 +24,21 @@ import { cn } from "@b3dotfun/sdk/shared/utils/cn";
24
24
  import { shortenAddress } from "@b3dotfun/sdk/shared/utils/formatAddress";
25
25
  import { formatDisplayNumber, formatTokenAmount } from "@b3dotfun/sdk/shared/utils/number";
26
26
  import invariant from "invariant";
27
- import { ArrowDown, ChevronLeft, ChevronRightCircle, ChevronsUpDown, CircleAlert, HistoryIcon } from "lucide-react";
27
+ import {
28
+ ArrowDown,
29
+ ChevronLeft,
30
+ ChevronRight,
31
+ ChevronRightCircle,
32
+ ChevronsUpDown,
33
+ CircleAlert,
34
+ HistoryIcon,
35
+ } from "lucide-react";
28
36
  import { motion } from "motion/react";
29
37
  import { useCallback, useEffect, useMemo, useRef, useState } from "react";
30
38
  import { toast } from "sonner";
39
+ import { useConnectedWallets } from "thirdweb/react";
31
40
  import { parseUnits } from "viem";
32
41
  import { b3Sepolia, base, mainnet, sepolia } from "viem/chains";
33
- import { useAccount } from "wagmi";
34
42
  import { components } from "../../types/api";
35
43
  import { AnySpendFingerprintWrapper, getFingerprintConfig } from "./AnySpendFingerprintWrapper";
36
44
  import { CryptoPaymentMethod, PaymentMethod } from "./common/CryptoPaymentMethod";
@@ -105,7 +113,7 @@ function AnySpendInner({
105
113
  const router = useRouter();
106
114
 
107
115
  // Get wagmi account state for wallet connection
108
- const wagmiAccount = useAccount();
116
+ const connectedWallets = useConnectedWallets();
109
117
 
110
118
  // Determine if we're in "buy mode" based on whether destination token props are provided
111
119
  const isBuyMode = !!(destinationTokenAddress && destinationTokenChainId);
@@ -464,6 +472,10 @@ function AnySpendInner({
464
472
  },
465
473
  );
466
474
 
475
+ const connectedAddress = globalAddress || connectedWallets?.[0]?.getAccount()?.address;
476
+ const connectedProfile = useProfile({ address: connectedAddress });
477
+ const connectedName = connectedProfile.data?.name?.replace(/\.b3\.fun/g, "");
478
+
467
479
  const recipientProfile = useProfile({ address: recipientAddress });
468
480
  const recipientName = recipientProfile.data?.name?.replace(/\.b3\.fun/g, "");
469
481
 
@@ -1071,32 +1083,38 @@ function AnySpendInner({
1071
1083
  <div className="flex items-center justify-between">
1072
1084
  <div className="text-as-primary/50 flex h-7 items-center text-sm">Pay</div>
1073
1085
  <button
1074
- className="text-as-primary/50 hover:text-as-primary/70 flex h-7 items-center gap-1 text-sm transition-colors"
1086
+ className="text-as-tertiarry flex h-7 items-center gap-2 text-sm transition-colors"
1075
1087
  onClick={() => setActivePanel(PanelView.CRYPTO_PAYMENT_METHOD)}
1076
1088
  >
1077
1089
  {selectedPaymentMethod === PaymentMethod.CONNECT_WALLET ? (
1078
1090
  <>
1079
- {globalAddress || wagmiAccount.address ? (
1091
+ {connectedAddress ? (
1080
1092
  <>
1081
1093
  {globalWallet?.meta?.icon && (
1082
- <img src={globalWallet.meta.icon} alt="Connected Wallet" className="h-4 w-4 rounded-full" />
1094
+ <img
1095
+ src={globalWallet.meta.icon}
1096
+ alt="Connected Wallet"
1097
+ className="bg-as-primary h-6 w-6 rounded-full"
1098
+ />
1083
1099
  )}
1084
- <span>{shortenAddress(globalAddress || wagmiAccount.address || "")}</span>
1100
+ <span className="text-as-tertiarry">
1101
+ {connectedName || shortenAddress(connectedAddress || "")}
1102
+ </span>
1085
1103
  </>
1086
1104
  ) : (
1087
1105
  "Connect wallet"
1088
1106
  )}
1089
- <ChevronRightCircle className="h-4 w-4" />
1107
+ <ChevronRight className="h-4 w-4" />
1090
1108
  </>
1091
1109
  ) : selectedPaymentMethod === PaymentMethod.TRANSFER_CRYPTO ? (
1092
1110
  <>
1093
1111
  Transfer crypto
1094
- <ChevronRightCircle className="h-4 w-4" />
1112
+ <ChevronRight className="h-4 w-4" />
1095
1113
  </>
1096
1114
  ) : (
1097
1115
  <>
1098
1116
  Select payment method
1099
- <ChevronRightCircle className="h-4 w-4" />
1117
+ <ChevronRight className="h-4 w-4" />
1100
1118
  </>
1101
1119
  )}
1102
1120
  </button>
@@ -1198,7 +1216,7 @@ function AnySpendInner({
1198
1216
  <div className="text-as-primary/50 flex h-7 items-center text-sm">Receive</div>
1199
1217
  {recipientAddress ? (
1200
1218
  <button
1201
- className={cn("text-as-primary/70 flex h-7 items-center gap-2 rounded-lg px-2")}
1219
+ className={cn("text-as-tertiarry flex h-7 items-center gap-2 rounded-lg")}
1202
1220
  onClick={() => setActivePanel(PanelView.RECIPIENT_SELECTION)}
1203
1221
  >
1204
1222
  {globalAddress && recipientAddress === globalAddress && globalWallet?.meta?.icon ? (
@@ -1213,7 +1231,7 @@ function AnySpendInner({
1213
1231
  </div>
1214
1232
  )}
1215
1233
  <div className="text-sm">{recipientName ? recipientName : shortenAddress(recipientAddress)}</div>
1216
- <ChevronRightCircle className="h-4 w-4" />
1234
+ <ChevronRight className="h-4 w-4" />
1217
1235
  </button>
1218
1236
  ) : (
1219
1237
  <button
@@ -1487,7 +1505,8 @@ function AnySpendInner({
1487
1505
  <div
1488
1506
  className={cn(
1489
1507
  "mx-auto w-full max-w-[460px]",
1490
- mode === "page" && "bg-as-surface-primary border-as-border-secondary rounded-2xl border p-6 shadow-xl",
1508
+ mode === "page" &&
1509
+ "bg-as-surface-primary border-as-border-secondary overflow-hidden rounded-2xl border shadow-xl",
1491
1510
  )}
1492
1511
  >
1493
1512
  <TransitionPanel
@@ -1500,7 +1519,7 @@ function AnySpendInner({
1500
1519
  ? PanelView.MAIN
1501
1520
  : activePanel
1502
1521
  }
1503
- className={cn("overflow-hidden", {
1522
+ className={cn("rounded-2xl", {
1504
1523
  "mt-0": mode === "modal",
1505
1524
  })}
1506
1525
  variants={{
@@ -1511,14 +1530,30 @@ function AnySpendInner({
1511
1530
  transition={{ type: "spring", stiffness: 300, damping: 30 }}
1512
1531
  >
1513
1532
  {[
1514
- <div key="main-view">{mainView}</div>,
1515
- <div key="history-view">{historyView}</div>,
1516
- <div key="order-details-view">{orderDetailsView}</div>,
1517
- <div key="loading-view">{OrderDetailsLoadingView}</div>,
1518
- <div key="fiat-payment-view">{onrampPaymentView}</div>,
1519
- <div key="recipient-selection-view">{recipientSelectionView}</div>,
1520
- <div key="crypto-payment-method-view">{cryptoPaymentMethodView}</div>,
1521
- <div key="fiat-payment-method-view">{fiatPaymentMethodView}</div>,
1533
+ <div key="main-view" className={cn(mode === "page" && "p-6")}>
1534
+ {mainView}
1535
+ </div>,
1536
+ <div key="history-view" className={cn(mode === "page" && "p-6")}>
1537
+ {historyView}
1538
+ </div>,
1539
+ <div key="order-details-view" className={cn(mode === "page" && "p-6")}>
1540
+ {orderDetailsView}
1541
+ </div>,
1542
+ <div key="loading-view" className={cn(mode === "page" && "p-6")}>
1543
+ {OrderDetailsLoadingView}
1544
+ </div>,
1545
+ <div key="fiat-payment-view" className={cn(mode === "page" && "p-6")}>
1546
+ {onrampPaymentView}
1547
+ </div>,
1548
+ <div key="recipient-selection-view" className={cn(mode === "page" && "p-6")}>
1549
+ {recipientSelectionView}
1550
+ </div>,
1551
+ <div key="crypto-payment-method-view" className={cn(mode === "page" && "p-6")}>
1552
+ {cryptoPaymentMethodView}
1553
+ </div>,
1554
+ <div key="fiat-payment-method-view" className={cn(mode === "page" && "p-6")}>
1555
+ {fiatPaymentMethodView}
1556
+ </div>,
1522
1557
  ]}
1523
1558
  </TransitionPanel>
1524
1559
  </div>
@@ -44,16 +44,12 @@ export function AnySpendFingerprintWrapper({ children, fingerprint }: AnySpendFi
44
44
  );
45
45
  }
46
46
 
47
- // Helper function to get fingerprint config from environment variables
48
- export function getFingerprintConfig(): FingerprintConfig | undefined {
49
- const apiKey = process.env.NEXT_PUBLIC_FINGERPRINT_API_KEY;
50
-
51
- if (!apiKey) {
52
- return undefined;
53
- }
47
+ const defaultApiKey = "80EnsS6POsxPAR9xGxmN";
54
48
 
49
+ // Helper function to get fingerprint config from environment variables
50
+ export function getFingerprintConfig(): FingerprintConfig {
55
51
  return {
56
- apiKey,
52
+ apiKey: process.env.NEXT_PUBLIC_FINGERPRINT_API_KEY || defaultApiKey,
57
53
  endpoint: process.env.NEXT_PUBLIC_FINGERPRINT_ENDPOINT,
58
54
  scriptUrlPattern: process.env.NEXT_PUBLIC_FINGERPRINT_SCRIPT_URL,
59
55
  };
@@ -1,13 +1,17 @@
1
1
  "use client";
2
2
 
3
3
  import { useAccountWallet } from "@b3dotfun/sdk/global-account/react";
4
+ import { thirdwebB3Mainnet } from "@b3dotfun/sdk/shared/constants/chains/b3Chain";
4
5
  import { cn } from "@b3dotfun/sdk/shared/utils/cn";
5
6
  import { shortenAddress } from "@b3dotfun/sdk/shared/utils/formatAddress";
7
+ import { client } from "@b3dotfun/sdk/shared/utils/thirdweb";
6
8
  import { ChevronLeft, ChevronRightCircle, Wallet, X } from "lucide-react";
7
- import { useEffect, useRef } from "react";
9
+ import { useState } from "react";
10
+ import { createPortal } from "react-dom";
8
11
  import { toast } from "sonner";
9
- import { useSetActiveWallet } from "thirdweb/react";
10
- import { useAccount, useConnect, useDisconnect } from "wagmi";
12
+ import { ConnectEmbed, lightTheme } from "thirdweb/react";
13
+ import { createWallet } from "thirdweb/wallets";
14
+ import { useDisconnect } from "wagmi";
11
15
 
12
16
  export enum PaymentMethod {
13
17
  NONE = "none",
@@ -36,59 +40,22 @@ export function CryptoPaymentMethod({
36
40
  onBack,
37
41
  onSelectPaymentMethod,
38
42
  }: CryptoPaymentMethodProps) {
39
- const { connect, connectors, isPending: isConnecting, error: connectError } = useConnect();
40
- const wagmiAccount = useAccount();
41
- const { address: globalAddress, connectedEOAWallet, isActiveEOAWallet, wallet: globalWallet } = useAccountWallet();
43
+ const { address: globalAddress, wallet: globalWallet } = useAccountWallet();
42
44
  const { disconnect } = useDisconnect();
43
- const previousAddress = useRef<string | undefined>(globalAddress);
45
+ const [showWalletModal, setShowWalletModal] = useState(false);
44
46
 
45
- const setActiveWallet = useSetActiveWallet();
46
-
47
- // Automatically set EOA wallet as active when available
48
- useEffect(() => {
49
- if (connectedEOAWallet) {
50
- console.log("Setting active wallet", connectedEOAWallet);
51
- setActiveWallet(connectedEOAWallet);
52
- }
53
- }, [connectedEOAWallet, isActiveEOAWallet, setActiveWallet]);
54
-
55
- // Handle wallet connection success
56
- useEffect(() => {
57
- if (globalAddress && previousAddress.current !== globalAddress) {
58
- previousAddress.current = globalAddress;
59
- toast.success("Wallet connected successfully");
60
- // Automatically select connect wallet method and go back to main view
61
- setSelectedPaymentMethod(PaymentMethod.CONNECT_WALLET);
62
- onSelectPaymentMethod(PaymentMethod.CONNECT_WALLET);
63
- }
64
- }, [globalAddress, setSelectedPaymentMethod, onSelectPaymentMethod]);
65
-
66
- // Handle connection errors
67
- useEffect(() => {
68
- if (connectError) {
69
- // Handle specific error cases
70
- if (connectError.message.includes("Connector already connected")) {
71
- // If connector is already connected, just proceed with the flow
72
- console.log("Connector already connected, proceeding with selection");
73
- // Use wagmi account address or global address
74
- if (wagmiAccount.address || globalAddress) {
75
- setSelectedPaymentMethod(PaymentMethod.CONNECT_WALLET);
76
- onSelectPaymentMethod(PaymentMethod.CONNECT_WALLET);
77
- } else {
78
- // Fallback: proceed anyway as the connector reports it's connected
79
- setTimeout(() => {
80
- setSelectedPaymentMethod(PaymentMethod.CONNECT_WALLET);
81
- onSelectPaymentMethod(PaymentMethod.CONNECT_WALLET);
82
- }, 100);
83
- }
84
- } else {
85
- toast.error(`Failed to connect wallet: ${connectError.message}`);
86
- }
87
- }
88
- }, [connectError, globalAddress, wagmiAccount.address, setSelectedPaymentMethod, onSelectPaymentMethod]);
47
+ // Define available wallets for the modal
48
+ const availableWallets = [
49
+ createWallet("io.metamask"),
50
+ // createWallet("com.coinbase.wallet"),
51
+ createWallet("me.rainbow"),
52
+ createWallet("walletConnect"),
53
+ createWallet("io.rabby"),
54
+ createWallet("app.phantom"),
55
+ ];
89
56
 
90
57
  return (
91
- <div className="mx-auto w-[460px] max-w-full">
58
+ <div className="mx-auto h-fit w-[460px] max-w-full">
92
59
  <div className={cn("relative flex flex-col gap-10")}>
93
60
  {/* Header */}
94
61
  <button
@@ -109,43 +76,8 @@ export function CryptoPaymentMethod({
109
76
  {!globalAddress ? (
110
77
  // Not connected - show single connect button
111
78
  <button
112
- onClick={() => {
113
- // Prevent connecting if already connecting or if there's already a connection
114
- if (isConnecting) return;
115
-
116
- try {
117
- // Check if wagmi already has a connection
118
- if (wagmiAccount.isConnected && wagmiAccount.address) {
119
- // Already connected via wagmi, just proceed with selection
120
- console.log("Wagmi already connected, proceeding with selection");
121
- setSelectedPaymentMethod(PaymentMethod.CONNECT_WALLET);
122
- onSelectPaymentMethod(PaymentMethod.CONNECT_WALLET);
123
- return;
124
- }
125
-
126
- // Check if global address exists (b3 account system)
127
- if (globalAddress) {
128
- // Already connected via global account, just proceed with selection
129
- console.log("Global address already exists, proceeding with selection");
130
- setSelectedPaymentMethod(PaymentMethod.CONNECT_WALLET);
131
- onSelectPaymentMethod(PaymentMethod.CONNECT_WALLET);
132
- return;
133
- }
134
-
135
- // Use the first available connector or a preferred one
136
- const preferredConnector =
137
- connectors.find(c => c.name.toLowerCase().includes("metamask")) || connectors[0];
138
-
139
- if (preferredConnector) {
140
- connect({ connector: preferredConnector });
141
- }
142
- } catch (error) {
143
- console.error("Connection error:", error);
144
- toast.error("Failed to connect wallet. Please try again.");
145
- }
146
- }}
147
- disabled={isConnecting}
148
- className="bg-as-surface-primary border-as-border-secondary hover:border-as-secondary/80 group flex w-full items-center justify-between gap-4 rounded-xl border px-4 py-3.5 transition-all duration-200 hover:shadow-md disabled:cursor-not-allowed disabled:opacity-50"
79
+ onClick={() => setShowWalletModal(true)}
80
+ className="bg-as-surface-primary border-as-border-secondary hover:border-as-secondary/80 group flex w-full items-center justify-between gap-4 rounded-xl border px-4 py-3.5 transition-all duration-200 hover:shadow-md"
149
81
  >
150
82
  <div className="flex items-center gap-3">
151
83
  <div className="flex h-8 w-8 items-center justify-center rounded-full bg-blue-100">
@@ -153,14 +85,10 @@ export function CryptoPaymentMethod({
153
85
  </div>
154
86
  <div className="flex flex-col items-start text-left">
155
87
  <h4 className="text-as-primary font-semibold">Connect wallet</h4>
156
- <p className="text-as-primary/60 text-sm">Connect your wallet to continue</p>
88
+ <p className="text-as-primary/60 text-sm">Choose from multiple wallet options</p>
157
89
  </div>
158
90
  </div>
159
- {isConnecting ? (
160
- <div className="border-as-primary/20 border-t-as-primary h-5 w-5 animate-spin rounded-full border-2"></div>
161
- ) : (
162
- <ChevronRightCircle className="text-as-primary/40 group-hover:text-as-primary/60 h-5 w-5 transition-colors" />
163
- )}
91
+ <ChevronRightCircle className="text-as-primary/40 group-hover:text-as-primary/60 h-5 w-5 transition-colors" />
164
92
  </button>
165
93
  ) : (
166
94
  // Connected - show wallet info
@@ -190,7 +118,7 @@ export function CryptoPaymentMethod({
190
118
  Use Wallet
191
119
  </button>
192
120
  <button
193
- onClick={() => {
121
+ onClick={async () => {
194
122
  disconnect();
195
123
  toast.success("Wallet disconnected");
196
124
  if (selectedPaymentMethod === PaymentMethod.CONNECT_WALLET) {
@@ -222,6 +150,43 @@ export function CryptoPaymentMethod({
222
150
  </button>
223
151
  </div>
224
152
  </div>
153
+
154
+ {/* Wallet Connection Modal */}
155
+ {showWalletModal &&
156
+ createPortal(
157
+ <div className="pointer-events-auto fixed inset-0 z-[9999] flex items-center justify-center bg-black/50">
158
+ <div className="max-h-[80vh] w-[400px] max-w-[90vw] overflow-auto rounded-xl bg-white p-6 dark:bg-gray-900">
159
+ <div className="mb-4 flex items-center justify-between">
160
+ <h3 className="text-lg font-semibold text-gray-900 dark:text-white">Connect Wallet</h3>
161
+ <button
162
+ onClick={() => setShowWalletModal(false)}
163
+ className="text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200"
164
+ >
165
+ <X className="h-5 w-5" />
166
+ </button>
167
+ </div>
168
+ <ConnectEmbed
169
+ client={client}
170
+ chain={thirdwebB3Mainnet}
171
+ wallets={availableWallets}
172
+ showThirdwebBranding={false}
173
+ theme={lightTheme()}
174
+ onConnect={async wallet => {
175
+ console.log("Wallet connected:", wallet);
176
+ // setShowWalletModal(false);
177
+ setSelectedPaymentMethod(PaymentMethod.CONNECT_WALLET);
178
+ onSelectPaymentMethod(PaymentMethod.CONNECT_WALLET);
179
+ setShowWalletModal(false);
180
+ }}
181
+ style={{
182
+ width: "100%",
183
+ minHeight: "300px",
184
+ }}
185
+ />
186
+ </div>
187
+ </div>,
188
+ typeof window !== "undefined" ? document.getElementById("b3-root") || document.body : document.body,
189
+ )}
225
190
  </div>
226
191
  );
227
192
  }
@@ -21,9 +21,9 @@ export { useOneBalance } from "./useOneBalance";
21
21
  export {
22
22
  useProfile,
23
23
  useProfilePreference,
24
- type Profile,
25
24
  type CombinedProfile,
26
25
  type PreferenceRequestBody,
26
+ type Profile,
27
27
  } from "./useProfile";
28
28
  export { useQueryB3 } from "./useQueryB3";
29
29
  export { useQueryBSMNT } from "./useQueryBSMNT";