@b3dotfun/sdk 0.0.62 → 0.0.63-alpha.1
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/anyspend/react/components/AnySpend.js +61 -23
- package/dist/cjs/anyspend/react/components/AnySpendCustom.js +3 -0
- package/dist/cjs/anyspend/react/components/AnySpendCustomExactIn.d.ts +34 -0
- package/dist/cjs/anyspend/react/components/AnySpendCustomExactIn.js +275 -0
- package/dist/cjs/anyspend/react/components/AnySpendStakeB3.js +5 -4
- package/dist/cjs/anyspend/react/components/AnySpendStakeB3ExactIn.d.ts +9 -0
- package/dist/cjs/anyspend/react/components/AnySpendStakeB3ExactIn.js +288 -0
- package/dist/cjs/anyspend/react/components/AnySpendStakeUpsideExactIn.d.ts +11 -0
- package/dist/cjs/anyspend/react/components/AnySpendStakeUpsideExactIn.js +33 -0
- package/dist/cjs/anyspend/react/components/AnyspendDepositHype.js +4 -4
- package/dist/cjs/anyspend/react/components/common/CryptoPaySection.js +4 -6
- package/dist/cjs/anyspend/react/components/common/CryptoPaymentMethod.js +9 -17
- package/dist/cjs/anyspend/react/components/common/CryptoReceiveSection.d.ts +6 -1
- package/dist/cjs/anyspend/react/components/common/CryptoReceiveSection.js +11 -1
- package/dist/cjs/anyspend/react/components/common/OrderDetails.js +66 -147
- package/dist/cjs/anyspend/react/components/common/OrderDetailsCollapsible.js +2 -3
- package/dist/cjs/anyspend/react/components/common/OrderTokenAmount.d.ts +2 -1
- package/dist/cjs/anyspend/react/components/common/OrderTokenAmount.js +39 -15
- package/dist/cjs/anyspend/react/components/common/PaySection.js +1 -1
- package/dist/cjs/anyspend/react/components/common/TokenBalance.js +1 -1
- package/dist/cjs/anyspend/react/components/index.d.ts +5 -1
- package/dist/cjs/anyspend/react/components/index.js +11 -3
- package/dist/cjs/anyspend/react/hooks/useAnyspendFlow.d.ts +25 -3
- package/dist/cjs/anyspend/react/hooks/useAnyspendFlow.js +42 -19
- package/dist/cjs/anyspend/react/hooks/useAnyspendOrderHistory.d.ts +116 -0
- package/dist/cjs/anyspend/react/hooks/useAnyspendQuote.js +1 -1
- package/dist/cjs/anyspend/react/hooks/useAutoSelectCryptoPaymentMethod.d.ts +26 -0
- package/dist/cjs/anyspend/react/hooks/useAutoSelectCryptoPaymentMethod.js +56 -0
- package/dist/cjs/anyspend/react/hooks/useAutoSetActiveWalletFromWagmi.d.ts +10 -0
- package/dist/cjs/anyspend/react/hooks/useAutoSetActiveWalletFromWagmi.js +73 -0
- package/dist/cjs/anyspend/react/hooks/useConnectedWalletDisplay.d.ts +14 -0
- package/dist/cjs/anyspend/react/hooks/useConnectedWalletDisplay.js +57 -0
- package/dist/cjs/anyspend/react/hooks/usePhantomTransfer.d.ts +36 -0
- package/dist/cjs/anyspend/react/hooks/usePhantomTransfer.js +211 -0
- package/dist/cjs/anyspend/types/api.d.ts +665 -3
- package/dist/cjs/anyspend/utils/orderPayload.js +4 -0
- package/dist/cjs/global-account/react/components/B3DynamicModal.js +17 -4
- package/dist/cjs/global-account/react/components/ManageAccount/BalanceContent.js +3 -3
- package/dist/cjs/global-account/react/components/ProfileEditor/ProfileEditor.d.ts +6 -0
- package/dist/cjs/global-account/react/components/ProfileEditor/ProfileEditor.js +141 -0
- package/dist/cjs/global-account/react/components/SignInWithB3/SignInWithB3Flow.js +3 -1
- package/dist/cjs/global-account/react/components/SignInWithB3/steps/LoginStep.js +2 -2
- package/dist/cjs/global-account/react/components/index.d.ts +2 -0
- package/dist/cjs/global-account/react/components/index.js +7 -2
- package/dist/cjs/global-account/react/hooks/index.d.ts +2 -1
- package/dist/cjs/global-account/react/hooks/index.js +5 -3
- package/dist/cjs/global-account/react/hooks/useAuthentication.d.ts +2 -2
- package/dist/cjs/global-account/react/hooks/useAuthentication.js +7 -2
- package/dist/cjs/global-account/react/hooks/useSimBalance.d.ts +1 -1
- package/dist/cjs/global-account/react/hooks/useSimBalance.js +6 -5
- package/dist/cjs/global-account/react/hooks/useTokenBalanceDirect.d.ts +12 -0
- package/dist/cjs/global-account/react/hooks/useTokenBalanceDirect.js +62 -0
- package/dist/cjs/global-account/react/hooks/useTokenFromUrl.js +4 -3
- package/dist/cjs/global-account/react/stores/useModalStore.d.ts +37 -1
- package/dist/cjs/global-account/react/utils/profileDisplay.d.ts +6 -0
- package/dist/cjs/global-account/react/utils/profileDisplay.js +60 -4
- package/dist/esm/anyspend/react/components/AnySpend.js +62 -24
- package/dist/esm/anyspend/react/components/AnySpendCustom.js +3 -0
- package/dist/esm/anyspend/react/components/AnySpendCustomExactIn.d.ts +34 -0
- package/dist/esm/anyspend/react/components/AnySpendCustomExactIn.js +269 -0
- package/dist/esm/anyspend/react/components/AnySpendStakeB3.js +7 -6
- package/dist/esm/anyspend/react/components/AnySpendStakeB3ExactIn.d.ts +9 -0
- package/dist/esm/anyspend/react/components/AnySpendStakeB3ExactIn.js +285 -0
- package/dist/esm/anyspend/react/components/AnySpendStakeUpsideExactIn.d.ts +11 -0
- package/dist/esm/anyspend/react/components/AnySpendStakeUpsideExactIn.js +30 -0
- package/dist/esm/anyspend/react/components/AnyspendDepositHype.js +4 -4
- package/dist/esm/anyspend/react/components/common/CryptoPaySection.js +5 -7
- package/dist/esm/anyspend/react/components/common/CryptoPaymentMethod.js +9 -17
- package/dist/esm/anyspend/react/components/common/CryptoReceiveSection.d.ts +6 -1
- package/dist/esm/anyspend/react/components/common/CryptoReceiveSection.js +11 -1
- package/dist/esm/anyspend/react/components/common/OrderDetails.js +67 -148
- package/dist/esm/anyspend/react/components/common/OrderDetailsCollapsible.js +2 -3
- package/dist/esm/anyspend/react/components/common/OrderTokenAmount.d.ts +2 -1
- package/dist/esm/anyspend/react/components/common/OrderTokenAmount.js +40 -16
- package/dist/esm/anyspend/react/components/common/PaySection.js +1 -1
- package/dist/esm/anyspend/react/components/common/TokenBalance.js +2 -2
- package/dist/esm/anyspend/react/components/index.d.ts +5 -1
- package/dist/esm/anyspend/react/components/index.js +5 -1
- package/dist/esm/anyspend/react/hooks/useAnyspendFlow.d.ts +25 -3
- package/dist/esm/anyspend/react/hooks/useAnyspendFlow.js +42 -19
- package/dist/esm/anyspend/react/hooks/useAnyspendOrderHistory.d.ts +116 -0
- package/dist/esm/anyspend/react/hooks/useAnyspendQuote.js +1 -1
- package/dist/esm/anyspend/react/hooks/useAutoSelectCryptoPaymentMethod.d.ts +26 -0
- package/dist/esm/anyspend/react/hooks/useAutoSelectCryptoPaymentMethod.js +53 -0
- package/dist/esm/anyspend/react/hooks/useAutoSetActiveWalletFromWagmi.d.ts +10 -0
- package/dist/esm/anyspend/react/hooks/useAutoSetActiveWalletFromWagmi.js +70 -0
- package/dist/esm/anyspend/react/hooks/useConnectedWalletDisplay.d.ts +14 -0
- package/dist/esm/anyspend/react/hooks/useConnectedWalletDisplay.js +54 -0
- package/dist/esm/anyspend/react/hooks/usePhantomTransfer.d.ts +36 -0
- package/dist/esm/anyspend/react/hooks/usePhantomTransfer.js +208 -0
- package/dist/esm/anyspend/types/api.d.ts +665 -3
- package/dist/esm/anyspend/utils/orderPayload.js +4 -0
- package/dist/esm/global-account/react/components/B3DynamicModal.js +18 -5
- package/dist/esm/global-account/react/components/ManageAccount/BalanceContent.js +3 -3
- package/dist/esm/global-account/react/components/ProfileEditor/ProfileEditor.d.ts +6 -0
- package/dist/esm/global-account/react/components/ProfileEditor/ProfileEditor.js +135 -0
- package/dist/esm/global-account/react/components/SignInWithB3/SignInWithB3Flow.js +3 -1
- package/dist/esm/global-account/react/components/SignInWithB3/steps/LoginStep.js +2 -2
- package/dist/esm/global-account/react/components/index.d.ts +2 -0
- package/dist/esm/global-account/react/components/index.js +3 -0
- package/dist/esm/global-account/react/hooks/index.d.ts +2 -1
- package/dist/esm/global-account/react/hooks/index.js +2 -1
- package/dist/esm/global-account/react/hooks/useAuthentication.d.ts +2 -2
- package/dist/esm/global-account/react/hooks/useAuthentication.js +7 -2
- package/dist/esm/global-account/react/hooks/useSimBalance.d.ts +1 -1
- package/dist/esm/global-account/react/hooks/useSimBalance.js +6 -5
- package/dist/esm/global-account/react/hooks/useTokenBalanceDirect.d.ts +12 -0
- package/dist/esm/global-account/react/hooks/useTokenBalanceDirect.js +59 -0
- package/dist/esm/global-account/react/hooks/useTokenFromUrl.js +4 -3
- package/dist/esm/global-account/react/stores/useModalStore.d.ts +37 -1
- package/dist/esm/global-account/react/utils/profileDisplay.d.ts +6 -0
- package/dist/esm/global-account/react/utils/profileDisplay.js +59 -4
- package/dist/types/anyspend/react/components/AnySpendCustomExactIn.d.ts +34 -0
- package/dist/types/anyspend/react/components/AnySpendStakeB3ExactIn.d.ts +9 -0
- package/dist/types/anyspend/react/components/AnySpendStakeUpsideExactIn.d.ts +11 -0
- package/dist/types/anyspend/react/components/common/CryptoReceiveSection.d.ts +6 -1
- package/dist/types/anyspend/react/components/common/OrderTokenAmount.d.ts +2 -1
- package/dist/types/anyspend/react/components/index.d.ts +5 -1
- package/dist/types/anyspend/react/hooks/useAnyspendFlow.d.ts +25 -3
- package/dist/types/anyspend/react/hooks/useAnyspendOrderHistory.d.ts +116 -0
- package/dist/types/anyspend/react/hooks/useAutoSelectCryptoPaymentMethod.d.ts +26 -0
- package/dist/types/anyspend/react/hooks/useAutoSetActiveWalletFromWagmi.d.ts +10 -0
- package/dist/types/anyspend/react/hooks/useConnectedWalletDisplay.d.ts +14 -0
- package/dist/types/anyspend/react/hooks/usePhantomTransfer.d.ts +36 -0
- package/dist/types/anyspend/types/api.d.ts +665 -3
- package/dist/types/global-account/react/components/ProfileEditor/ProfileEditor.d.ts +6 -0
- package/dist/types/global-account/react/components/index.d.ts +2 -0
- package/dist/types/global-account/react/hooks/index.d.ts +2 -1
- package/dist/types/global-account/react/hooks/useAuthentication.d.ts +2 -2
- package/dist/types/global-account/react/hooks/useSimBalance.d.ts +1 -1
- package/dist/types/global-account/react/hooks/useTokenBalanceDirect.d.ts +12 -0
- package/dist/types/global-account/react/stores/useModalStore.d.ts +37 -1
- package/dist/types/global-account/react/utils/profileDisplay.d.ts +6 -0
- package/package.json +4 -3
- package/src/anyspend/react/components/AnySpend.tsx +73 -22
- package/src/anyspend/react/components/AnySpendCustom.tsx +4 -0
- package/src/anyspend/react/components/AnySpendCustomExactIn.tsx +595 -0
- package/src/anyspend/react/components/AnySpendStakeB3.tsx +8 -11
- package/src/anyspend/react/components/AnySpendStakeB3ExactIn.tsx +522 -0
- package/src/anyspend/react/components/AnySpendStakeUpsideExactIn.tsx +73 -0
- package/src/anyspend/react/components/AnyspendDepositHype.tsx +7 -3
- package/src/anyspend/react/components/common/CryptoPaySection.tsx +5 -7
- package/src/anyspend/react/components/common/CryptoPaymentMethod.tsx +9 -18
- package/src/anyspend/react/components/common/CryptoReceiveSection.tsx +22 -0
- package/src/anyspend/react/components/common/OrderDetails.tsx +76 -190
- package/src/anyspend/react/components/common/OrderDetailsCollapsible.tsx +2 -3
- package/src/anyspend/react/components/common/OrderTokenAmount.tsx +48 -17
- package/src/anyspend/react/components/common/PaySection.tsx +1 -0
- package/src/anyspend/react/components/common/TokenBalance.tsx +2 -2
- package/src/anyspend/react/components/index.ts +5 -1
- package/src/anyspend/react/hooks/useAnyspendFlow.ts +51 -18
- package/src/anyspend/react/hooks/useAnyspendQuote.ts +1 -1
- package/src/anyspend/react/hooks/useAutoSelectCryptoPaymentMethod.ts +72 -0
- package/src/anyspend/react/hooks/useAutoSetActiveWalletFromWagmi.ts +80 -0
- package/src/anyspend/react/hooks/useConnectedWalletDisplay.ts +69 -0
- package/src/anyspend/react/hooks/usePhantomTransfer.ts +301 -0
- package/src/anyspend/types/api.ts +669 -1
- package/src/anyspend/utils/orderPayload.ts +5 -1
- package/src/global-account/react/components/B3DynamicModal.tsx +18 -4
- package/src/global-account/react/components/ManageAccount/BalanceContent.tsx +4 -4
- package/src/global-account/react/components/ProfileEditor/ProfileEditor.tsx +265 -0
- package/src/global-account/react/components/SignInWithB3/SignInWithB3Flow.tsx +3 -1
- package/src/global-account/react/components/SignInWithB3/steps/LoginStep.tsx +2 -2
- package/src/global-account/react/components/index.ts +4 -0
- package/src/global-account/react/hooks/index.ts +2 -1
- package/src/global-account/react/hooks/useAuthentication.ts +10 -2
- package/src/global-account/react/hooks/useSimBalance.ts +6 -5
- package/src/global-account/react/hooks/useTokenBalanceDirect.tsx +84 -0
- package/src/global-account/react/hooks/useTokenFromUrl.tsx +6 -5
- package/src/global-account/react/stores/useModalStore.ts +43 -1
- package/src/global-account/react/utils/profileDisplay.ts +67 -4
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { normalizeAddress } from "@b3dotfun/sdk/anyspend/utils";
|
|
2
1
|
import { components } from "@b3dotfun/sdk/anyspend/types/api";
|
|
2
|
+
import { normalizeAddress } from "@b3dotfun/sdk/anyspend/utils";
|
|
3
3
|
|
|
4
4
|
export type OrderParams = {
|
|
5
5
|
orderType: components["schemas"]["Order"]["type"];
|
|
@@ -48,6 +48,8 @@ export const buildPayload = (orderType: components["schemas"]["Order"]["type"],
|
|
|
48
48
|
};
|
|
49
49
|
case "custom":
|
|
50
50
|
return { ...payload };
|
|
51
|
+
case "custom_exact_in":
|
|
52
|
+
return { ...payload, expectedDstAmount };
|
|
51
53
|
case "hype_duel":
|
|
52
54
|
return {
|
|
53
55
|
expectedDstAmount,
|
|
@@ -73,6 +75,8 @@ export const buildMetadata = (orderType: components["schemas"]["Order"]["type"],
|
|
|
73
75
|
return { ...baseMetadata, tournament };
|
|
74
76
|
case "custom":
|
|
75
77
|
return { ...baseMetadata, action: payload.action };
|
|
78
|
+
case "custom_exact_in":
|
|
79
|
+
return { ...baseMetadata, action: payload.action };
|
|
76
80
|
case "hype_duel":
|
|
77
81
|
return { ...baseMetadata };
|
|
78
82
|
default:
|
|
@@ -5,25 +5,28 @@ import {
|
|
|
5
5
|
AnySpendNFT,
|
|
6
6
|
AnyspendSignatureMint,
|
|
7
7
|
AnySpendStakeB3,
|
|
8
|
+
AnySpendStakeB3ExactIn,
|
|
8
9
|
AnySpendTournament,
|
|
9
10
|
OrderHistory,
|
|
10
11
|
} from "@b3dotfun/sdk/anyspend/react";
|
|
11
12
|
import { AnySpendDepositHype } from "@b3dotfun/sdk/anyspend/react/components/AnyspendDepositHype";
|
|
12
13
|
import { AnySpendStakeUpside } from "@b3dotfun/sdk/anyspend/react/components/AnySpendStakeUpside";
|
|
14
|
+
import { AnySpendStakeUpsideExactIn } from "@b3dotfun/sdk/anyspend/react/components/AnySpendStakeUpsideExactIn";
|
|
13
15
|
import { useGlobalWalletState } from "@b3dotfun/sdk/anyspend/utils";
|
|
14
16
|
import { useIsMobile, useModalStore } from "@b3dotfun/sdk/global-account/react";
|
|
15
17
|
import { cn } from "@b3dotfun/sdk/shared/utils/cn";
|
|
16
18
|
import { debugB3React } from "@b3dotfun/sdk/shared/utils/debug";
|
|
17
19
|
import { useEffect, useRef } from "react";
|
|
20
|
+
import { useSetActiveWallet } from "thirdweb/react";
|
|
18
21
|
import { AvatarEditor } from "./AvatarEditor/AvatarEditor";
|
|
19
22
|
import { useB3 } from "./B3Provider/useB3";
|
|
20
23
|
import { LinkAccount } from "./LinkAccount/LinkAccount";
|
|
24
|
+
import { ProfileEditor } from "./ProfileEditor/ProfileEditor";
|
|
21
25
|
import { ManageAccount } from "./ManageAccount/ManageAccount";
|
|
22
26
|
import { RequestPermissions } from "./RequestPermissions/RequestPermissions";
|
|
23
27
|
import { SignInWithB3Flow } from "./SignInWithB3/SignInWithB3Flow";
|
|
24
28
|
import { Dialog, DialogContent, DialogDescription, DialogTitle } from "./ui/dialog";
|
|
25
29
|
import { Drawer, DrawerContent, DrawerDescription, DrawerTitle } from "./ui/drawer";
|
|
26
|
-
import { useSetActiveWallet } from "thirdweb/react";
|
|
27
30
|
|
|
28
31
|
const debug = debugB3React("B3DynamicModal");
|
|
29
32
|
|
|
@@ -56,7 +59,9 @@ export function B3DynamicModal() {
|
|
|
56
59
|
"anySpendJoinTournament",
|
|
57
60
|
"anySpendFundTournament",
|
|
58
61
|
"anySpendStakeB3",
|
|
62
|
+
"anySpendStakeB3ExactIn",
|
|
59
63
|
"anySpendStakeUpside",
|
|
64
|
+
"anySpendStakeUpsideExactIn",
|
|
60
65
|
"anySpendBuySpin",
|
|
61
66
|
"anySpendOrderHistory",
|
|
62
67
|
"signInWithB3",
|
|
@@ -64,6 +69,7 @@ export function B3DynamicModal() {
|
|
|
64
69
|
"anySpendBondKit",
|
|
65
70
|
"linkAccount",
|
|
66
71
|
"avatarEditor",
|
|
72
|
+
"profileEditor",
|
|
67
73
|
];
|
|
68
74
|
|
|
69
75
|
const freestyleTypes = [
|
|
@@ -71,7 +77,9 @@ export function B3DynamicModal() {
|
|
|
71
77
|
"anySpendJoinTournament",
|
|
72
78
|
"anySpendFundTournament",
|
|
73
79
|
"anySpendStakeB3",
|
|
80
|
+
"anySpendStakeB3ExactIn",
|
|
74
81
|
"anySpendStakeUpside",
|
|
82
|
+
"anySpendStakeUpsideExactIn",
|
|
75
83
|
"anySpendBuySpin",
|
|
76
84
|
"anySpendSignatureMint",
|
|
77
85
|
"anySpendBondKit",
|
|
@@ -120,8 +128,12 @@ export function B3DynamicModal() {
|
|
|
120
128
|
return <OrderHistory onBack={() => {}} mode="modal" />;
|
|
121
129
|
case "anySpendStakeB3":
|
|
122
130
|
return <AnySpendStakeB3 {...contentType} mode="modal" />;
|
|
131
|
+
case "anySpendStakeB3ExactIn":
|
|
132
|
+
return <AnySpendStakeB3ExactIn {...contentType} mode="modal" />;
|
|
123
133
|
case "anySpendStakeUpside":
|
|
124
134
|
return <AnySpendStakeUpside {...contentType} mode="modal" />;
|
|
135
|
+
case "anySpendStakeUpsideExactIn":
|
|
136
|
+
return <AnySpendStakeUpsideExactIn {...contentType} mode="modal" />;
|
|
125
137
|
case "anySpendBuySpin":
|
|
126
138
|
return <AnySpendBuySpin {...contentType} mode="modal" />;
|
|
127
139
|
case "anySpendSignatureMint":
|
|
@@ -134,6 +146,8 @@ export function B3DynamicModal() {
|
|
|
134
146
|
return <AnySpendDepositHype {...contentType} mode="modal" />;
|
|
135
147
|
case "avatarEditor":
|
|
136
148
|
return <AvatarEditor onSetAvatar={contentType.onSuccess} />;
|
|
149
|
+
case "profileEditor":
|
|
150
|
+
return <ProfileEditor onSuccess={contentType.onSuccess} />;
|
|
137
151
|
// Add other modal types here
|
|
138
152
|
default:
|
|
139
153
|
return null;
|
|
@@ -152,8 +166,8 @@ export function B3DynamicModal() {
|
|
|
152
166
|
contentClass,
|
|
153
167
|
"rounded-2xl bg-white shadow-xl dark:bg-gray-900",
|
|
154
168
|
"border border-gray-200 dark:border-gray-800",
|
|
155
|
-
// Remove default width classes for avatar editor
|
|
156
|
-
contentType?.type === "avatarEditor"
|
|
169
|
+
// Remove default width classes for avatar editor and profile editor
|
|
170
|
+
contentType?.type === "avatarEditor" || contentType?.type === "profileEditor"
|
|
157
171
|
? "!w-[90vw] !max-w-none" // Use !important to override default styles
|
|
158
172
|
: "mx-auto w-full max-w-md sm:max-w-lg",
|
|
159
173
|
)}
|
|
@@ -189,7 +203,7 @@ export function B3DynamicModal() {
|
|
|
189
203
|
{renderContent()}
|
|
190
204
|
</div>
|
|
191
205
|
</ModalContent>
|
|
192
|
-
{contentType?.type === "avatarEditor" && (
|
|
206
|
+
{(contentType?.type === "avatarEditor" || contentType?.type === "profileEditor") && (
|
|
193
207
|
<button
|
|
194
208
|
onClick={() => setB3ModalOpen(false)}
|
|
195
209
|
className="fixed right-5 top-5 z-[100] cursor-pointer text-gray-600 hover:text-gray-900 dark:text-gray-400 dark:hover:text-white"
|
|
@@ -50,10 +50,10 @@ export function BalanceContent({ onLogout, showDeposit = true, showSwap = true }
|
|
|
50
50
|
|
|
51
51
|
const avatarUrl = user?.avatar ? getIpfsUrl(user?.avatar) : profile?.avatar;
|
|
52
52
|
|
|
53
|
-
const
|
|
53
|
+
const handleEditProfile = () => {
|
|
54
54
|
setB3ModalOpen(true);
|
|
55
55
|
setB3ModalContentType({
|
|
56
|
-
type: "
|
|
56
|
+
type: "profileEditor",
|
|
57
57
|
showBackButton: true,
|
|
58
58
|
onSuccess: () => {
|
|
59
59
|
// navigate back on success
|
|
@@ -125,7 +125,7 @@ export function BalanceContent({ onLogout, showDeposit = true, showSwap = true }
|
|
|
125
125
|
<div className="bg-b3-primary-wash size-24 rounded-full" />
|
|
126
126
|
)}
|
|
127
127
|
<button
|
|
128
|
-
onClick={
|
|
128
|
+
onClick={handleEditProfile}
|
|
129
129
|
className="bg-b3-grey border-b3-background hover:bg-b3-grey/80 absolute -bottom-1 -right-1 flex size-8 items-center justify-center rounded-full border-4 transition-colors"
|
|
130
130
|
>
|
|
131
131
|
<Pencil size={16} className="text-b3-background" />
|
|
@@ -133,7 +133,7 @@ export function BalanceContent({ onLogout, showDeposit = true, showSwap = true }
|
|
|
133
133
|
</div>
|
|
134
134
|
<div className="global-account-profile-info">
|
|
135
135
|
<h2 className="text-b3-grey text-xl font-semibold">
|
|
136
|
-
{profile?.displayName || formatUsername(profile?.name || "")}
|
|
136
|
+
{user?.username || profile?.displayName || formatUsername(profile?.name || "")}
|
|
137
137
|
</h2>
|
|
138
138
|
<div className="address-button 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">
|
|
139
139
|
<span className="text-b3-foreground-muted font-mono text-xs">
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { Users } from "@b3dotfun/b3-api";
|
|
4
|
+
import app from "@b3dotfun/sdk/global-account/app";
|
|
5
|
+
import { Button, useB3, useProfile } from "@b3dotfun/sdk/global-account/react";
|
|
6
|
+
import { validateImageUrl } from "@b3dotfun/sdk/global-account/react/utils/profileDisplay";
|
|
7
|
+
import { cn } from "@b3dotfun/sdk/shared/utils/cn";
|
|
8
|
+
import { debugB3React } from "@b3dotfun/sdk/shared/utils/debug";
|
|
9
|
+
import { getIpfsUrl } from "@b3dotfun/sdk/shared/utils/ipfs";
|
|
10
|
+
import { client } from "@b3dotfun/sdk/shared/utils/thirdweb";
|
|
11
|
+
import { Check, Loader2, Upload, X } from "lucide-react";
|
|
12
|
+
import { useRef, useState } from "react";
|
|
13
|
+
import { toast } from "sonner";
|
|
14
|
+
import { useActiveAccount } from "thirdweb/react";
|
|
15
|
+
import { upload } from "thirdweb/storage";
|
|
16
|
+
|
|
17
|
+
const debug = debugB3React("ProfileEditor");
|
|
18
|
+
|
|
19
|
+
interface ProfileEditorProps {
|
|
20
|
+
onSuccess?: () => void;
|
|
21
|
+
className?: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function ProfileEditor({ onSuccess, className }: ProfileEditorProps) {
|
|
25
|
+
const [selectedFile, setSelectedFile] = useState<File | null>(null);
|
|
26
|
+
const [previewUrl, setPreviewUrl] = useState<string | null>(null);
|
|
27
|
+
const [username, setUsername] = useState<string>("");
|
|
28
|
+
const [isUploading, setIsUploading] = useState(false);
|
|
29
|
+
const [isSaving, setIsSaving] = useState(false);
|
|
30
|
+
const fileInputRef = useRef<HTMLInputElement>(null);
|
|
31
|
+
const { user, setUser } = useB3();
|
|
32
|
+
|
|
33
|
+
const account = useActiveAccount();
|
|
34
|
+
const { data: profile, refetch: refreshProfile } = useProfile({
|
|
35
|
+
address: account?.address,
|
|
36
|
+
fresh: true,
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const rawAvatarUrl = user?.avatar ? getIpfsUrl(user?.avatar) : profile?.avatar;
|
|
40
|
+
const avatarUrl = validateImageUrl(rawAvatarUrl);
|
|
41
|
+
const safePreviewUrl = validateImageUrl(previewUrl);
|
|
42
|
+
const hasAvatar = !!avatarUrl;
|
|
43
|
+
const currentUsername = user?.username || "";
|
|
44
|
+
|
|
45
|
+
const handleFileSelect = (event: React.ChangeEvent<HTMLInputElement>) => {
|
|
46
|
+
const file = event.target.files?.[0];
|
|
47
|
+
if (file) {
|
|
48
|
+
// Validate file type
|
|
49
|
+
if (!file.type.startsWith("image/")) {
|
|
50
|
+
toast.error("Please select an image file");
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Validate file size (max 5MB)
|
|
55
|
+
if (file.size > 5 * 1024 * 1024) {
|
|
56
|
+
toast.error("File size must be less than 5MB");
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
setSelectedFile(file);
|
|
61
|
+
|
|
62
|
+
// Create preview URL
|
|
63
|
+
const url = URL.createObjectURL(file);
|
|
64
|
+
setPreviewUrl(url);
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const handleRemoveFile = () => {
|
|
69
|
+
setSelectedFile(null);
|
|
70
|
+
if (previewUrl) {
|
|
71
|
+
URL.revokeObjectURL(previewUrl);
|
|
72
|
+
setPreviewUrl(null);
|
|
73
|
+
}
|
|
74
|
+
if (fileInputRef.current) {
|
|
75
|
+
fileInputRef.current.value = "";
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const handleSave = async () => {
|
|
80
|
+
// Check if there are any changes
|
|
81
|
+
const hasAvatarChange = selectedFile !== null;
|
|
82
|
+
const hasUsernameChange = username.trim() !== "" && username !== currentUsername;
|
|
83
|
+
|
|
84
|
+
if (!hasAvatarChange && !hasUsernameChange) {
|
|
85
|
+
toast.error("Please make at least one change");
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
setIsSaving(true);
|
|
90
|
+
try {
|
|
91
|
+
let ipfsUrl: string | undefined;
|
|
92
|
+
|
|
93
|
+
// Upload avatar if selected
|
|
94
|
+
if (hasAvatarChange && selectedFile) {
|
|
95
|
+
debug("Starting upload to IPFS", selectedFile);
|
|
96
|
+
setIsUploading(true);
|
|
97
|
+
|
|
98
|
+
ipfsUrl = await upload({
|
|
99
|
+
client,
|
|
100
|
+
files: [selectedFile],
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
debug("Upload successful", ipfsUrl);
|
|
104
|
+
setIsUploading(false);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Update user profile
|
|
108
|
+
let updatedUser = user as Users | undefined;
|
|
109
|
+
|
|
110
|
+
// If both avatar and username need updating, do them sequentially
|
|
111
|
+
// Update avatar first if uploaded
|
|
112
|
+
if (ipfsUrl) {
|
|
113
|
+
// @ts-expect-error this resolved fine, look into why expect-error needed
|
|
114
|
+
updatedUser = await app.service("users").setAvatar(
|
|
115
|
+
{
|
|
116
|
+
avatar: ipfsUrl,
|
|
117
|
+
},
|
|
118
|
+
// @ts-expect-error - our typed client is expecting context even though it's set elsewhere
|
|
119
|
+
{},
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Update username if changed (this will use the updated user from avatar change if both were updated)
|
|
124
|
+
if (hasUsernameChange && user?._id) {
|
|
125
|
+
// @ts-expect-error this resolved fine, look into why expect-error needed
|
|
126
|
+
updatedUser = await app.service("users").registerUsername(
|
|
127
|
+
{ username: username },
|
|
128
|
+
// @ts-expect-error - our typed client is expecting context even though it's set elsewhere
|
|
129
|
+
{},
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Update user state
|
|
134
|
+
setUser(updatedUser);
|
|
135
|
+
|
|
136
|
+
// Refresh profile to get updated data
|
|
137
|
+
await refreshProfile();
|
|
138
|
+
|
|
139
|
+
// Show success message
|
|
140
|
+
const changes = [];
|
|
141
|
+
if (hasAvatarChange) changes.push("avatar");
|
|
142
|
+
if (hasUsernameChange) changes.push("username");
|
|
143
|
+
toast.success(`Successfully updated ${changes.join(" and ")}!`);
|
|
144
|
+
|
|
145
|
+
onSuccess?.();
|
|
146
|
+
|
|
147
|
+
// Clean up
|
|
148
|
+
handleRemoveFile();
|
|
149
|
+
setUsername("");
|
|
150
|
+
} catch (error) {
|
|
151
|
+
debug("Error updating profile:", error);
|
|
152
|
+
toast.error("Failed to update profile. Please try again.");
|
|
153
|
+
} finally {
|
|
154
|
+
setIsUploading(false);
|
|
155
|
+
setIsSaving(false);
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
const handleFileInputClick = () => {
|
|
160
|
+
fileInputRef.current?.click();
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
const isLoading = isUploading || isSaving;
|
|
164
|
+
const hasChanges = selectedFile !== null || (username.trim() !== "" && username !== currentUsername);
|
|
165
|
+
|
|
166
|
+
return (
|
|
167
|
+
<div className={cn("flex flex-col items-center justify-center space-y-6 p-8", className)}>
|
|
168
|
+
<div className="space-y-2 text-center">
|
|
169
|
+
<h2 className="font-neue-montreal-semibold text-b3-grey text-2xl">Edit Your Profile</h2>
|
|
170
|
+
<p className="text-b3-foreground-muted font-neue-montreal-medium">Update your avatar and username</p>
|
|
171
|
+
</div>
|
|
172
|
+
|
|
173
|
+
{/* Avatar Section */}
|
|
174
|
+
<div className="w-full max-w-md space-y-4">
|
|
175
|
+
<div className="space-y-2">
|
|
176
|
+
<label className="text-b3-grey font-neue-montreal-semibold text-sm">Avatar</label>
|
|
177
|
+
|
|
178
|
+
{/* Current/Preview Avatar */}
|
|
179
|
+
<div className="flex justify-center">
|
|
180
|
+
{safePreviewUrl || avatarUrl ? (
|
|
181
|
+
<div className="relative">
|
|
182
|
+
<div className="border-b3-primary-blue h-32 w-32 overflow-hidden rounded-full border-4">
|
|
183
|
+
<img
|
|
184
|
+
src={safePreviewUrl || avatarUrl || ""}
|
|
185
|
+
alt={safePreviewUrl ? "Preview" : "Current avatar"}
|
|
186
|
+
className="h-full w-full object-cover"
|
|
187
|
+
/>
|
|
188
|
+
</div>
|
|
189
|
+
{safePreviewUrl && (
|
|
190
|
+
<button
|
|
191
|
+
onClick={handleRemoveFile}
|
|
192
|
+
className="bg-b3-negative absolute -right-2 -top-2 flex h-8 w-8 items-center justify-center rounded-full text-white transition-colors hover:bg-red-600"
|
|
193
|
+
disabled={isLoading}
|
|
194
|
+
>
|
|
195
|
+
<X size={16} />
|
|
196
|
+
</button>
|
|
197
|
+
)}
|
|
198
|
+
</div>
|
|
199
|
+
) : (
|
|
200
|
+
<div className="bg-b3-primary-wash h-32 w-32 rounded-full" />
|
|
201
|
+
)}
|
|
202
|
+
</div>
|
|
203
|
+
|
|
204
|
+
{/* Upload Button */}
|
|
205
|
+
{!selectedFile && (
|
|
206
|
+
<Button variant="outline" onClick={handleFileInputClick} disabled={isLoading} className="w-full">
|
|
207
|
+
<Upload className="mr-2 h-4 w-4" />
|
|
208
|
+
{hasAvatar ? "Change Avatar" : "Upload Avatar"}
|
|
209
|
+
</Button>
|
|
210
|
+
)}
|
|
211
|
+
|
|
212
|
+
{/* Hidden file input */}
|
|
213
|
+
<input ref={fileInputRef} type="file" accept="image/*" onChange={handleFileSelect} className="hidden" />
|
|
214
|
+
</div>
|
|
215
|
+
|
|
216
|
+
{/* Username Section */}
|
|
217
|
+
<div className="space-y-2">
|
|
218
|
+
<label htmlFor="username" className="text-b3-grey font-neue-montreal-semibold text-sm">
|
|
219
|
+
Username
|
|
220
|
+
</label>
|
|
221
|
+
<input
|
|
222
|
+
id="username"
|
|
223
|
+
type="text"
|
|
224
|
+
value={username}
|
|
225
|
+
onChange={e => setUsername(e.target.value)}
|
|
226
|
+
placeholder={currentUsername || "Enter username"}
|
|
227
|
+
className="border-b3-line bg-b3-background text-b3-grey placeholder:text-b3-foreground-muted font-neue-montreal-medium focus:border-b3-primary-blue w-full rounded-lg border px-4 py-3 transition-colors focus:outline-none"
|
|
228
|
+
disabled={isLoading}
|
|
229
|
+
/>
|
|
230
|
+
{currentUsername && (
|
|
231
|
+
<p className="text-b3-foreground-muted font-neue-montreal-medium text-xs">Current: {currentUsername}</p>
|
|
232
|
+
)}
|
|
233
|
+
</div>
|
|
234
|
+
</div>
|
|
235
|
+
|
|
236
|
+
{/* Action Buttons */}
|
|
237
|
+
<div className="flex w-full max-w-md gap-3">
|
|
238
|
+
<Button
|
|
239
|
+
onClick={handleSave}
|
|
240
|
+
disabled={isLoading || !hasChanges}
|
|
241
|
+
className="bg-b3-primary-blue hover:bg-b3-primary-blue/90 flex-1 text-white disabled:opacity-50"
|
|
242
|
+
>
|
|
243
|
+
{isLoading ? (
|
|
244
|
+
<>
|
|
245
|
+
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
|
246
|
+
{isUploading ? "Uploading..." : "Saving..."}
|
|
247
|
+
</>
|
|
248
|
+
) : (
|
|
249
|
+
<>
|
|
250
|
+
<Check className="mr-2 h-4 w-4" />
|
|
251
|
+
Save Changes
|
|
252
|
+
</>
|
|
253
|
+
)}
|
|
254
|
+
</Button>
|
|
255
|
+
</div>
|
|
256
|
+
|
|
257
|
+
{/* Help Text */}
|
|
258
|
+
<div className="text-b3-foreground-muted font-neue-montreal-medium max-w-md text-center text-xs">
|
|
259
|
+
<p>
|
|
260
|
+
Your avatar will be uploaded to IPFS and stored securely. Make sure you have the rights to use this image.
|
|
261
|
+
</p>
|
|
262
|
+
</div>
|
|
263
|
+
</div>
|
|
264
|
+
);
|
|
265
|
+
}
|
|
@@ -39,6 +39,7 @@ export function SignInWithB3Flow({
|
|
|
39
39
|
const { setB3ModalContentType, setB3ModalOpen, isOpen } = useModalStore();
|
|
40
40
|
const account = useActiveAccount();
|
|
41
41
|
const isAuthenticating = useAuthStore(state => state.isAuthenticating);
|
|
42
|
+
const isAuthenticated = useAuthStore(state => state.isAuthenticated);
|
|
42
43
|
const isConnected = useAuthStore(state => state.isConnected);
|
|
43
44
|
const [refetchCount, setRefetchCount] = useState(0);
|
|
44
45
|
const [refetchError, setRefetchError] = useState<string | null>(null);
|
|
@@ -92,7 +93,7 @@ export function SignInWithB3Flow({
|
|
|
92
93
|
source,
|
|
93
94
|
});
|
|
94
95
|
|
|
95
|
-
if (isConnected) {
|
|
96
|
+
if (isConnected && isAuthenticated) {
|
|
96
97
|
// Check if we already have a signer for this partner
|
|
97
98
|
const hasExistingSigner = signers?.some(signer => signer.partner.id === partnerId);
|
|
98
99
|
if (hasExistingSigner) {
|
|
@@ -139,6 +140,7 @@ export function SignInWithB3Flow({
|
|
|
139
140
|
signersEnabled,
|
|
140
141
|
isConnected,
|
|
141
142
|
isAuthenticating,
|
|
143
|
+
isAuthenticated,
|
|
142
144
|
isOpen,
|
|
143
145
|
]);
|
|
144
146
|
|
|
@@ -86,8 +86,8 @@ export function LoginStep({ onSuccess, chain }: LoginStepProps) {
|
|
|
86
86
|
titleIcon: "https://cdn.b3.fun/b3_logo.svg",
|
|
87
87
|
}}
|
|
88
88
|
className="b3-login-step"
|
|
89
|
-
onConnect={async wallet => {
|
|
90
|
-
await onConnect(wallet);
|
|
89
|
+
onConnect={async (wallet, allConnectedWallets) => {
|
|
90
|
+
await onConnect(wallet, allConnectedWallets);
|
|
91
91
|
const account = wallet.getAccount();
|
|
92
92
|
if (!account) throw new Error("No account found");
|
|
93
93
|
await onSuccess(account);
|
|
@@ -19,6 +19,10 @@ export { getConnectOptionsFromStrategy, isWalletType, type AllowedStrategy } fro
|
|
|
19
19
|
// ManageAccount Components
|
|
20
20
|
export { ManageAccount } from "./ManageAccount/ManageAccount";
|
|
21
21
|
|
|
22
|
+
// Profile Components
|
|
23
|
+
export { AvatarEditor } from "./AvatarEditor/AvatarEditor";
|
|
24
|
+
export { ProfileEditor } from "./ProfileEditor/ProfileEditor";
|
|
25
|
+
|
|
22
26
|
// RequestPermissions Components
|
|
23
27
|
export { RequestPermissions } from "./RequestPermissions/RequestPermissions";
|
|
24
28
|
export { RequestPermissionsButton } from "./RequestPermissions/RequestPermissionsButton";
|
|
@@ -38,11 +38,12 @@ export { useSearchParamsSSR } from "./useSearchParamsSSR";
|
|
|
38
38
|
export { useSimBalance } from "./useSimBalance";
|
|
39
39
|
export { useSiwe } from "./useSiwe";
|
|
40
40
|
export { useTokenBalance } from "./useTokenBalance";
|
|
41
|
+
export { useTokenBalanceDirect } from "./useTokenBalanceDirect";
|
|
41
42
|
export { useTokenBalancesByChain } from "./useTokenBalancesByChain";
|
|
42
43
|
export { useTokenData } from "./useTokenData";
|
|
43
44
|
export { useTokenFromUrl } from "./useTokenFromUrl";
|
|
44
45
|
export { useTokenPrice } from "./useTokenPrice";
|
|
45
46
|
export { useTokenPriceWithFallback } from "./useTokenPriceWithFallback";
|
|
46
47
|
export { useTokensFromAddress } from "./useTokensFromAddress";
|
|
47
|
-
export { useUnifiedChainSwitchAndExecute } from "./useUnifiedChainSwitchAndExecute";
|
|
48
48
|
export { useURLParams } from "./useURLParams";
|
|
49
|
+
export { useUnifiedChainSwitchAndExecute } from "./useUnifiedChainSwitchAndExecute";
|
|
@@ -164,7 +164,15 @@ export function useAuthentication(partnerId: string) {
|
|
|
164
164
|
);
|
|
165
165
|
|
|
166
166
|
const onConnect = useCallback(
|
|
167
|
-
async (
|
|
167
|
+
async (_walleAutoConnectedWith: Wallet, allConnectedWallets: Wallet[]) => {
|
|
168
|
+
debug("@@useAuthentication:onConnect", { _walleAutoConnectedWith, allConnectedWallets });
|
|
169
|
+
|
|
170
|
+
const wallet = allConnectedWallets.find(wallet => wallet.id.startsWith("ecosystem."));
|
|
171
|
+
|
|
172
|
+
if (!wallet) {
|
|
173
|
+
throw new Error("No smart wallet found during auto-connect");
|
|
174
|
+
}
|
|
175
|
+
|
|
168
176
|
debug("@@useAuthentication:onConnect", { wallet });
|
|
169
177
|
|
|
170
178
|
try {
|
|
@@ -243,7 +251,7 @@ export function useAuthentication(partnerId: string) {
|
|
|
243
251
|
const { isLoading: useAutoConnectLoading } = useAutoConnect({
|
|
244
252
|
client,
|
|
245
253
|
wallets: [wallet],
|
|
246
|
-
onConnect
|
|
254
|
+
onConnect,
|
|
247
255
|
});
|
|
248
256
|
|
|
249
257
|
/**
|
|
@@ -26,10 +26,11 @@ export interface SimBalanceResponse {
|
|
|
26
26
|
balances: SimBalanceItem[];
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
async function fetchSimBalance(address: string): Promise<SimBalanceResponse> {
|
|
29
|
+
async function fetchSimBalance(address: string, chainIdsParam: number[]): Promise<SimBalanceResponse> {
|
|
30
30
|
if (!address) throw new Error("Address is required");
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
const chainIds = chainIdsParam.length === 0 ? "mainnet" : chainIdsParam.join(",");
|
|
33
|
+
let url = `https://simdune-api.sean-430.workers.dev/?url=https://api.sim.dune.com/v1/evm/balances/${address}?metadata=logo&chain_ids=${chainIds}`;
|
|
33
34
|
if (process.env.NEXT_PUBLIC_LOCAL_KEY) {
|
|
34
35
|
url += `&localkey=${process.env.NEXT_PUBLIC_LOCAL_KEY}`;
|
|
35
36
|
}
|
|
@@ -44,12 +45,12 @@ async function fetchSimBalance(address: string): Promise<SimBalanceResponse> {
|
|
|
44
45
|
return balanceData;
|
|
45
46
|
}
|
|
46
47
|
|
|
47
|
-
export function useSimBalance(address?: string) {
|
|
48
|
+
export function useSimBalance(address?: string, chainIdsParam?: number[]) {
|
|
48
49
|
return useQuery({
|
|
49
|
-
queryKey: ["simBalance", address],
|
|
50
|
+
queryKey: ["simBalance", address, chainIdsParam],
|
|
50
51
|
queryFn: () => {
|
|
51
52
|
if (!address) throw new Error("Address is required");
|
|
52
|
-
return fetchSimBalance(address);
|
|
53
|
+
return fetchSimBalance(address, chainIdsParam || []);
|
|
53
54
|
},
|
|
54
55
|
enabled: Boolean(address),
|
|
55
56
|
});
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { isNativeToken } from "@b3dotfun/sdk/anyspend";
|
|
4
|
+
import { components } from "@b3dotfun/sdk/anyspend/types/api";
|
|
5
|
+
import { useAccountWallet } from "@b3dotfun/sdk/global-account/react";
|
|
6
|
+
import { formatTokenAmount } from "@b3dotfun/sdk/shared/utils/number";
|
|
7
|
+
import { getERC20Balances, getNativeTokenBalance } from "@b3dotfun/sdk/shared/utils/thirdweb-insights";
|
|
8
|
+
import { useQuery } from "@tanstack/react-query";
|
|
9
|
+
import { useEffect } from "react";
|
|
10
|
+
|
|
11
|
+
interface UseTokenBalanceProps {
|
|
12
|
+
token: components["schemas"]["Token"];
|
|
13
|
+
address?: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface TokenBalanceResult {
|
|
17
|
+
rawBalance: bigint | null;
|
|
18
|
+
formattedBalance: string;
|
|
19
|
+
isLoading: boolean;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function useTokenBalanceDirect({ token, address }: UseTokenBalanceProps): TokenBalanceResult {
|
|
23
|
+
const account = useAccountWallet();
|
|
24
|
+
|
|
25
|
+
const effectiveAddress = address || account?.address;
|
|
26
|
+
|
|
27
|
+
const {
|
|
28
|
+
data: tokenBalance,
|
|
29
|
+
isLoading,
|
|
30
|
+
isFetching,
|
|
31
|
+
refetch,
|
|
32
|
+
} = useQuery({
|
|
33
|
+
queryKey: ["tokenBalance", effectiveAddress, token.chainId, token.address],
|
|
34
|
+
queryFn: async (): Promise<{ formatted: string; raw: bigint | null }> => {
|
|
35
|
+
if (!effectiveAddress) return { formatted: "0", raw: null };
|
|
36
|
+
|
|
37
|
+
if (isNativeToken(token.address)) {
|
|
38
|
+
const nativeToken = await getNativeTokenBalance(effectiveAddress, token.chainId);
|
|
39
|
+
if (nativeToken && nativeToken.balance) {
|
|
40
|
+
const rawBalance = nativeToken.balance;
|
|
41
|
+
return {
|
|
42
|
+
formatted: formatTokenAmount(BigInt(rawBalance), Number(nativeToken.decimals || 18)),
|
|
43
|
+
raw: BigInt(rawBalance),
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
return { formatted: "0", raw: null };
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const response = await getERC20Balances(effectiveAddress, {
|
|
50
|
+
chainIds: [token.chainId],
|
|
51
|
+
includeSpam: false,
|
|
52
|
+
});
|
|
53
|
+
const tokenBalance = response.data?.find(t => t.token_address === token.address);
|
|
54
|
+
if (tokenBalance?.balance) {
|
|
55
|
+
return {
|
|
56
|
+
formatted: formatTokenAmount(BigInt(tokenBalance.balance), Number(tokenBalance.decimals || 18)),
|
|
57
|
+
raw: BigInt(tokenBalance.balance),
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
return { formatted: "0", raw: null };
|
|
61
|
+
},
|
|
62
|
+
enabled: !!effectiveAddress,
|
|
63
|
+
staleTime: 30000,
|
|
64
|
+
gcTime: 5 * 60 * 1000,
|
|
65
|
+
retry: 2,
|
|
66
|
+
structuralSharing: false,
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
// Force a refetch when the wallet or token changes
|
|
70
|
+
useEffect(() => {
|
|
71
|
+
if (effectiveAddress) {
|
|
72
|
+
refetch();
|
|
73
|
+
}
|
|
74
|
+
}, [effectiveAddress, token.address, token.chainId, token.symbol, refetch]);
|
|
75
|
+
|
|
76
|
+
// Determine if we're actually loading
|
|
77
|
+
const isActuallyLoading = !effectiveAddress || isLoading || (isFetching && !tokenBalance);
|
|
78
|
+
|
|
79
|
+
return {
|
|
80
|
+
rawBalance: tokenBalance?.raw || BigInt(0),
|
|
81
|
+
formattedBalance: tokenBalance?.formatted || "0",
|
|
82
|
+
isLoading: isActuallyLoading,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
+
import { components } from "@b3dotfun/sdk/anyspend/types/api";
|
|
3
4
|
import { getCoingeckoChainInfo } from "@b3dotfun/sdk/shared/constants/chains/supported";
|
|
4
5
|
import { useSearchParams } from "@b3dotfun/sdk/shared/react/hooks";
|
|
5
6
|
import { useQuery } from "@tanstack/react-query";
|
|
6
|
-
import { components } from "@b3dotfun/sdk/anyspend/types/api";
|
|
7
7
|
|
|
8
8
|
interface UseTokenFromUrlOptions {
|
|
9
9
|
/**
|
|
@@ -59,14 +59,15 @@ export function useTokenFromUrl({ defaultToken, prefix }: UseTokenFromUrlOptions
|
|
|
59
59
|
const currencyParam = searchParams.get(`${prefix}Currency`);
|
|
60
60
|
const chainIdParam = searchParams.get(`${prefix}ChainId`);
|
|
61
61
|
|
|
62
|
+
// Determine network based on chainId
|
|
63
|
+
const chainInfo = chainIdParam ? getCoingeckoChainInfo(Number(chainIdParam)) : null;
|
|
64
|
+
const network = chainInfo?.coingecko_id || "";
|
|
65
|
+
|
|
62
66
|
// Determine if we should fetch token info
|
|
63
67
|
const shouldFetchToken = Boolean(
|
|
64
|
-
currencyParam && chainIdParam && currencyParam.toLowerCase() !== defaultToken.address.toLowerCase(),
|
|
68
|
+
currencyParam && chainIdParam && chainInfo && currencyParam.toLowerCase() !== defaultToken.address.toLowerCase(),
|
|
65
69
|
);
|
|
66
70
|
|
|
67
|
-
// Determine network based on chainId
|
|
68
|
-
const network = chainIdParam ? getCoingeckoChainInfo(Number(chainIdParam)).coingecko_id : "";
|
|
69
|
-
|
|
70
71
|
const { data: tokenInfo, isError } = useQuery({
|
|
71
72
|
queryKey: ["tokenInfo", network, currencyParam],
|
|
72
73
|
queryFn: () => fetchTokenInfo(network, currencyParam || ""),
|