@b3dotfun/sdk 0.0.88-alpha.7 → 0.0.88-alpha.9
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/constants/index.d.ts +2 -0
- package/dist/cjs/anyspend/constants/index.js +3 -1
- package/dist/cjs/anyspend/index.d.ts +2 -0
- package/dist/cjs/anyspend/index.js +3 -0
- package/dist/cjs/anyspend/react/components/AnySpend.js +4 -1
- package/dist/cjs/anyspend/react/components/AnySpendCustomExactIn.js +22 -18
- package/dist/cjs/anyspend/react/components/common/GasIndicator.d.ts +6 -0
- package/dist/cjs/anyspend/react/components/common/GasIndicator.js +34 -0
- package/dist/cjs/anyspend/react/hooks/index.d.ts +1 -0
- package/dist/cjs/anyspend/react/hooks/index.js +1 -0
- package/dist/cjs/anyspend/react/hooks/useGasPrice.d.ts +37 -0
- package/dist/cjs/anyspend/react/hooks/useGasPrice.js +43 -0
- package/dist/cjs/anyspend/services/gas.d.ts +21 -0
- package/dist/cjs/anyspend/services/gas.js +65 -0
- package/dist/cjs/anyspend/types/gas.d.ts +61 -0
- package/dist/cjs/anyspend/types/gas.js +2 -0
- package/dist/cjs/global-account/react/components/B3Provider/B3Provider.js +14 -2
- package/dist/cjs/global-account/react/hooks/useTokenBalancesByChain.js +4 -2
- package/dist/esm/anyspend/constants/index.d.ts +2 -0
- package/dist/esm/anyspend/constants/index.js +2 -0
- package/dist/esm/anyspend/index.d.ts +2 -0
- package/dist/esm/anyspend/index.js +3 -0
- package/dist/esm/anyspend/react/components/AnySpend.js +5 -2
- package/dist/esm/anyspend/react/components/AnySpendCustomExactIn.js +5 -1
- package/dist/esm/anyspend/react/components/common/GasIndicator.d.ts +6 -0
- package/dist/esm/anyspend/react/components/common/GasIndicator.js +31 -0
- package/dist/esm/anyspend/react/hooks/index.d.ts +1 -0
- package/dist/esm/anyspend/react/hooks/index.js +1 -0
- package/dist/esm/anyspend/react/hooks/useGasPrice.d.ts +37 -0
- package/dist/esm/anyspend/react/hooks/useGasPrice.js +40 -0
- package/dist/esm/anyspend/services/gas.d.ts +21 -0
- package/dist/esm/anyspend/services/gas.js +59 -0
- package/dist/esm/anyspend/types/gas.d.ts +61 -0
- package/dist/esm/anyspend/types/gas.js +1 -0
- package/dist/esm/global-account/react/components/B3Provider/B3Provider.js +14 -2
- package/dist/esm/global-account/react/hooks/useTokenBalancesByChain.js +4 -2
- package/dist/styles/index.css +1 -1
- package/dist/types/anyspend/constants/index.d.ts +2 -0
- package/dist/types/anyspend/index.d.ts +2 -0
- package/dist/types/anyspend/react/components/common/GasIndicator.d.ts +6 -0
- package/dist/types/anyspend/react/hooks/index.d.ts +1 -0
- package/dist/types/anyspend/react/hooks/useGasPrice.d.ts +37 -0
- package/dist/types/anyspend/services/gas.d.ts +21 -0
- package/dist/types/anyspend/types/gas.d.ts +61 -0
- package/package.json +1 -1
- package/src/anyspend/constants/index.ts +3 -0
- package/src/anyspend/index.ts +4 -0
- package/src/anyspend/react/components/AnySpend.tsx +10 -0
- package/src/anyspend/react/components/AnySpendCustomExactIn.tsx +10 -0
- package/src/anyspend/react/components/common/GasIndicator.tsx +59 -0
- package/src/anyspend/react/hooks/index.ts +1 -0
- package/src/anyspend/react/hooks/useGasPrice.ts +70 -0
- package/src/anyspend/services/gas.test.ts +31 -0
- package/src/anyspend/services/gas.ts +73 -0
- package/src/anyspend/types/gas.ts +66 -0
- package/src/global-account/react/components/B3Provider/B3Provider.tsx +14 -2
- package/src/global-account/react/hooks/useTokenBalancesByChain.tsx +4 -2
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// Types
|
|
2
2
|
export * from "./types/api.js";
|
|
3
|
+
export * from "./types/gas.js";
|
|
3
4
|
// Utils
|
|
4
5
|
export * from "./utils/address.js";
|
|
5
6
|
export * from "./utils/chain.js";
|
|
@@ -11,6 +12,8 @@ export * from "./utils/token.js";
|
|
|
11
12
|
export * from "./utils/validation.js";
|
|
12
13
|
// Constants
|
|
13
14
|
export * from "./constants/index.js";
|
|
15
|
+
// Services
|
|
16
|
+
export * from "./services/gas.js";
|
|
14
17
|
// Abis
|
|
15
18
|
export * from "./abis/abiUsdcBase.js";
|
|
16
19
|
export * from "./abis/erc20Staking.js";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { eqci, getDefaultToken, getHyperliquidUSDCToken, HYPERLIQUID_CHAIN_ID, HYPERLIQUID_USDC_ADDRESS, USDC_BASE, ZERO_ADDRESS, } from "../../../anyspend/index.js";
|
|
4
|
-
import { useAnyspendCreateOnrampOrder, useAnyspendCreateOrder, useAnyspendOrderAndTransactions, useAnyspendQuote, useGeoOnrampOptions, } from "../../../anyspend/react/index.js";
|
|
4
|
+
import { useAnyspendCreateOnrampOrder, useAnyspendCreateOrder, useAnyspendOrderAndTransactions, useAnyspendQuote, useGasPrice, useGeoOnrampOptions, } from "../../../anyspend/react/index.js";
|
|
5
5
|
import { Button, ShinyButton, StyleRoot, TabsPrimitive, toast, TransitionPanel, useAccountWallet, useB3Config, useModalStore, useProfile, useRouter, useSearchParamsSSR, useTokenBalanceDirect, useTokenData, useTokenFromUrl, } from "../../../global-account/react/index.js";
|
|
6
6
|
import BottomNavigation from "../../../global-account/react/components/ManageAccount/BottomNavigation.js";
|
|
7
7
|
import { useAccountWalletImage } from "../../../global-account/react/hooks/useAccountWallet.js";
|
|
@@ -24,6 +24,7 @@ import { CryptoPaySection } from "./common/CryptoPaySection.js";
|
|
|
24
24
|
import { CryptoReceiveSection } from "./common/CryptoReceiveSection.js";
|
|
25
25
|
import { FeeDetailPanel } from "./common/FeeDetailPanel.js";
|
|
26
26
|
import { FiatPaymentMethod, FiatPaymentMethodComponent } from "./common/FiatPaymentMethod.js";
|
|
27
|
+
import { GasIndicator } from "./common/GasIndicator.js";
|
|
27
28
|
import { OrderDetails, OrderDetailsLoadingView } from "./common/OrderDetails.js";
|
|
28
29
|
import { OrderHistory } from "./common/OrderHistory.js";
|
|
29
30
|
import { PanelOnramp } from "./common/PanelOnramp.js";
|
|
@@ -365,6 +366,8 @@ function AnySpendInner({ sourceChainId, destinationTokenAddress, destinationToke
|
|
|
365
366
|
});
|
|
366
367
|
// Get geo-based onramp options for fiat payments
|
|
367
368
|
const { geoData, coinbaseAvailablePaymentMethods, stripeOnrampSupport, stripeWeb2Support } = useGeoOnrampOptions(srcAmountOnRamp);
|
|
369
|
+
// Get gas price for source chain (where the user pays from)
|
|
370
|
+
const { gasPrice: gasPriceData, isLoading: isLoadingGas } = useGasPrice(selectedSrcChainId);
|
|
368
371
|
// Helper function to map payment method to onramp vendor
|
|
369
372
|
const getOnrampVendor = (paymentMethod) => {
|
|
370
373
|
switch (paymentMethod) {
|
|
@@ -853,7 +856,7 @@ function AnySpendInner({ sourceChainId, destinationTokenAddress, destinationToke
|
|
|
853
856
|
}, children: _jsx("div", { className: "relative flex items-center justify-center transition-opacity", children: _jsx(ArrowDown, { className: "text-as-primary/50 h-5 w-5" }) }) }) })), activeTab === "crypto" && (_jsx(CryptoReceiveSection, { isDepositMode: false, isBuyMode: isBuyMode, effectiveRecipientAddress: effectiveRecipientAddress, recipientName: recipientName || undefined, customRecipientLabel: customRecipientLabel, onSelectRecipient: () => navigateToPanel(PanelView.RECIPIENT_SELECTION, "forward"), dstAmount: dstAmount, dstToken: selectedDstToken, selectedDstChainId: selectedDstChainId, setSelectedDstChainId: setSelectedDstChainId, setSelectedDstToken: setSelectedDstToken, isSrcInputDirty: isSrcInputDirty, onChangeDstAmount: value => {
|
|
854
857
|
setIsSrcInputDirty(false);
|
|
855
858
|
setDstAmount(value);
|
|
856
|
-
}, anyspendQuote: anyspendQuote, onShowPointsDetail: () => navigateToPanel(PanelView.POINTS_DETAIL, "forward"), onShowFeeDetail: () => navigateToPanel(PanelView.FEE_DETAIL, "forward") }))] }), _jsxs(motion.div, { initial: { opacity: 0, y: 20, filter: "blur(10px)" }, animate: { opacity: 1, y: 0, filter: "blur(0px)" }, transition: { duration: 0.3, delay: 0.2, ease: "easeInOut" }, className: cn("mt-4 flex w-full max-w-[460px] flex-col gap-2"), children: [_jsx(ShinyButton, { accentColor: "hsl(var(--as-brand))", disabled: btnInfo.disable, onClick: onMainButtonClick, className: cn("as-main-button relative w-full", btnInfo.error ? "!bg-as-red" : btnInfo.disable ? "!bg-as-on-surface-2" : "!bg-as-brand"), textClassName: cn(btnInfo.error ? "text-white" : btnInfo.disable ? "text-as-secondary" : "text-white"), children: _jsxs("div", { className: "flex items-center justify-center gap-2", children: [btnInfo.loading && _jsx(Loader2, { className: "h-4 w-4 animate-spin" }), btnInfo.text] }) }), !hideTransactionHistoryButton && (globalAddress || effectiveRecipientAddress) ? (_jsxs(Button, { variant: "link", onClick: onClickHistory, className: "text-as-primary/50 hover:text-as-primary flex items-center gap-1 transition-colors", children: [_jsx(HistoryIcon, { className: "h-4 w-4" }), " ", _jsx("span", { className: "pr-4", children: "Transaction History" })] })) : null] })] }), _jsx("div", { className: "w-full", children: _jsx(TabsPrimitive, { defaultValue: "swap", onValueChange: value => {
|
|
859
|
+
}, anyspendQuote: anyspendQuote, onShowPointsDetail: () => navigateToPanel(PanelView.POINTS_DETAIL, "forward"), onShowFeeDetail: () => navigateToPanel(PanelView.FEE_DETAIL, "forward") }))] }), gasPriceData && !isLoadingGas && activeTab === "crypto" && (_jsx(GasIndicator, { gasPrice: gasPriceData, className: "mt-2 w-full" })), _jsxs(motion.div, { initial: { opacity: 0, y: 20, filter: "blur(10px)" }, animate: { opacity: 1, y: 0, filter: "blur(0px)" }, transition: { duration: 0.3, delay: 0.2, ease: "easeInOut" }, className: cn("mt-4 flex w-full max-w-[460px] flex-col gap-2"), children: [_jsx(ShinyButton, { accentColor: "hsl(var(--as-brand))", disabled: btnInfo.disable, onClick: onMainButtonClick, className: cn("as-main-button relative w-full", btnInfo.error ? "!bg-as-red" : btnInfo.disable ? "!bg-as-on-surface-2" : "!bg-as-brand"), textClassName: cn(btnInfo.error ? "text-white" : btnInfo.disable ? "text-as-secondary" : "text-white"), children: _jsxs("div", { className: "flex items-center justify-center gap-2", children: [btnInfo.loading && _jsx(Loader2, { className: "h-4 w-4 animate-spin" }), btnInfo.text] }) }), !hideTransactionHistoryButton && (globalAddress || effectiveRecipientAddress) ? (_jsxs(Button, { variant: "link", onClick: onClickHistory, className: "text-as-primary/50 hover:text-as-primary flex items-center gap-1 transition-colors", children: [_jsx(HistoryIcon, { className: "h-4 w-4" }), " ", _jsx("span", { className: "pr-4", children: "Transaction History" })] })) : null] })] }), _jsx("div", { className: "w-full", children: _jsx(TabsPrimitive, { defaultValue: "swap", onValueChange: value => {
|
|
857
860
|
if (value === "settings" || value === "home") {
|
|
858
861
|
setB3ModalContentType({
|
|
859
862
|
type: "manageAccount",
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useGasPrice } from "../../../anyspend/react/index.js";
|
|
2
3
|
import { normalizeAddress } from "../../../anyspend/utils/index.js";
|
|
3
4
|
import { Button, ShinyButton, StyleRoot, toast, TransitionPanel, useAccountWallet, } from "../../../global-account/react/index.js";
|
|
4
5
|
import { cn } from "../../../shared/utils/cn.js";
|
|
@@ -16,6 +17,7 @@ import { CryptoPaymentMethod, CryptoPaymentMethodType } from "./common/CryptoPay
|
|
|
16
17
|
import { CryptoReceiveSection } from "./common/CryptoReceiveSection.js";
|
|
17
18
|
import { FeeDetailPanel } from "./common/FeeDetailPanel.js";
|
|
18
19
|
import { FiatPaymentMethod, FiatPaymentMethodComponent } from "./common/FiatPaymentMethod.js";
|
|
20
|
+
import { GasIndicator } from "./common/GasIndicator.js";
|
|
19
21
|
import { OrderDetails } from "./common/OrderDetails.js";
|
|
20
22
|
import { PanelOnramp } from "./common/PanelOnramp.js";
|
|
21
23
|
import { PointsDetailPanel } from "./common/PointsDetailPanel.js";
|
|
@@ -46,6 +48,8 @@ function AnySpendCustomExactInInner({ loadOrder, mode = "modal", recipientAddres
|
|
|
46
48
|
});
|
|
47
49
|
const { connectedEOAWallet } = useAccountWallet();
|
|
48
50
|
const setActiveWallet = useSetActiveWallet();
|
|
51
|
+
// Get gas price for source chain (where the user pays from)
|
|
52
|
+
const { gasPrice: gasPriceData, isLoading: isLoadingGas } = useGasPrice(selectedSrcChainId);
|
|
49
53
|
const appliedPreferEoa = useRef(false);
|
|
50
54
|
useEffect(() => {
|
|
51
55
|
if (preferEoa && !appliedPreferEoa.current) {
|
|
@@ -178,7 +182,7 @@ function AnySpendCustomExactInInner({ loadOrder, mode = "modal", recipientAddres
|
|
|
178
182
|
const mainView = (_jsxs("div", { className: "anyspend-custom-exact-in-container mx-auto flex w-[460px] max-w-full flex-col items-center gap-2", children: [headerContent, _jsx("div", { className: "relative flex w-full max-w-[calc(100vw-32px)] flex-col gap-2", children: _jsxs("div", { className: "relative flex w-full max-w-[calc(100vw-32px)] flex-col gap-2", children: [paymentType === "crypto" ? (_jsx(CryptoPaySection, { selectedSrcChainId: selectedSrcChainId, setSelectedSrcChainId: setSelectedSrcChainId, selectedSrcToken: selectedSrcToken, setSelectedSrcToken: setSelectedSrcToken, srcAmount: srcAmount, setSrcAmount: setSrcAmount, isSrcInputDirty: isSrcInputDirty, setIsSrcInputDirty: setIsSrcInputDirty, selectedCryptoPaymentMethod: effectiveCryptoPaymentMethod, onSelectCryptoPaymentMethod: () => setActivePanel(PanelView.CRYPTO_PAYMENT_METHOD), anyspendQuote: anyspendQuote, onTokenSelect: onTokenSelect })) : (_jsx(motion.div, { initial: { opacity: 0, y: 20, filter: "blur(10px)" }, animate: { opacity: 1, y: 0, filter: "blur(0px)" }, transition: { duration: 0.3, delay: 0, ease: "easeInOut" }, children: _jsx(PanelOnramp, { srcAmountOnRamp: srcAmount, setSrcAmountOnRamp: setSrcAmount, selectedPaymentMethod: selectedFiatPaymentMethod, setActivePanel: setActivePanel, _recipientAddress: selectedRecipientOrDefault, destinationToken: selectedDstToken, destinationChainId: selectedDstChainId, dstTokenSymbol: DESTINATION_TOKEN_DETAILS.SYMBOL, hideDstToken: true, destinationAmount: dstAmount, onDestinationTokenChange: () => { }, onDestinationChainChange: () => { }, fiatPaymentMethodIndex: PanelView.FIAT_PAYMENT_METHOD, recipientSelectionPanelIndex: PanelView.RECIPIENT_SELECTION, anyspendQuote: anyspendQuote, onShowPointsDetail: () => setActivePanel(PanelView.POINTS_DETAIL), onShowFeeDetail: () => setActivePanel(PanelView.FEE_DETAIL), customUsdInputValues: customUsdInputValues, customRecipientLabel: customRecipientLabel }) })), _jsx("div", { className: cn("relative -my-1 flex h-0 items-center justify-center", paymentType === "fiat" && "hidden"), children: _jsx(Button, { variant: "ghost", className: cn("swap-direction-button border-as-stroke bg-as-surface-primary z-10 h-10 w-10 cursor-default rounded-xl border-2 sm:h-8 sm:w-8 sm:rounded-xl"), children: _jsx("div", { className: "relative flex items-center justify-center transition-opacity", children: _jsx(ArrowDown, { className: "text-as-primary/50 h-5 w-5" }) }) }) }), paymentType === "crypto" && (_jsx(CryptoReceiveSection, { isDepositMode: false, isBuyMode: true, effectiveRecipientAddress: selectedRecipientOrDefault, recipientName: recipientName || undefined, customRecipientLabel: customRecipientLabel, onSelectRecipient: () => setActivePanel(PanelView.RECIPIENT_SELECTION), dstAmount: dstAmount, dstToken: selectedDstToken, dstTokenSymbol: DESTINATION_TOKEN_DETAILS.SYMBOL, dstTokenLogoURI: DESTINATION_TOKEN_DETAILS.LOGO_URI, selectedDstChainId: selectedDstChainId, setSelectedDstChainId: () => { }, setSelectedDstToken: () => { }, isSrcInputDirty: isSrcInputDirty, onChangeDstAmount: value => {
|
|
179
183
|
setIsSrcInputDirty(false);
|
|
180
184
|
setSrcAmount(value);
|
|
181
|
-
}, anyspendQuote: anyspendQuote, onShowPointsDetail: () => setActivePanel(PanelView.POINTS_DETAIL), onShowFeeDetail: () => setActivePanel(PanelView.FEE_DETAIL) }))] }) }), _jsx(motion.div, { initial: { opacity: 0, y: 20, filter: "blur(10px)" }, animate: { opacity: 1, y: 0, filter: "blur(0px)" }, transition: { duration: 0.3, delay: 0.2, ease: "easeInOut" }, className: cn("mt-4 flex w-full max-w-[460px] flex-col gap-2"), children: _jsx(ShinyButton, { accentColor: "hsl(var(--as-brand))", disabled: btnInfo.disable, onClick: onMainButtonClick, className: cn("as-main-button relative w-full", btnInfo.error ? "!bg-as-red" : btnInfo.disable ? "!bg-as-on-surface-2" : "!bg-as-brand"), textClassName: cn(btnInfo.error ? "text-white" : btnInfo.disable ? "text-as-secondary" : "text-white"), children: _jsxs("div", { className: "flex items-center justify-center gap-2", children: [btnInfo.loading && _jsx(Loader2, { className: "h-4 w-4 animate-spin" }), btnInfo.text] }) }) }), mainFooter ? mainFooter : null] }));
|
|
185
|
+
}, anyspendQuote: anyspendQuote, onShowPointsDetail: () => setActivePanel(PanelView.POINTS_DETAIL), onShowFeeDetail: () => setActivePanel(PanelView.FEE_DETAIL) }))] }) }), _jsx(motion.div, { initial: { opacity: 0, y: 20, filter: "blur(10px)" }, animate: { opacity: 1, y: 0, filter: "blur(0px)" }, transition: { duration: 0.3, delay: 0.2, ease: "easeInOut" }, className: cn("mt-4 flex w-full max-w-[460px] flex-col gap-2"), children: _jsx(ShinyButton, { accentColor: "hsl(var(--as-brand))", disabled: btnInfo.disable, onClick: onMainButtonClick, className: cn("as-main-button relative w-full", btnInfo.error ? "!bg-as-red" : btnInfo.disable ? "!bg-as-on-surface-2" : "!bg-as-brand"), textClassName: cn(btnInfo.error ? "text-white" : btnInfo.disable ? "text-as-secondary" : "text-white"), children: _jsxs("div", { className: "flex items-center justify-center gap-2", children: [btnInfo.loading && _jsx(Loader2, { className: "h-4 w-4 animate-spin" }), btnInfo.text] }) }) }), gasPriceData && !isLoadingGas && paymentType === "crypto" && (_jsx(GasIndicator, { gasPrice: gasPriceData, className: "mt-2 w-full" })), mainFooter ? mainFooter : null] }));
|
|
182
186
|
const handleCryptoOrder = async () => {
|
|
183
187
|
try {
|
|
184
188
|
invariant(anyspendQuote, "Relay price is not found");
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { GasPriceData } from "../../../types/gas";
|
|
2
|
+
export interface GasIndicatorProps {
|
|
3
|
+
gasPrice: GasPriceData;
|
|
4
|
+
className?: string;
|
|
5
|
+
}
|
|
6
|
+
export declare function GasIndicator({ gasPrice, className }: GasIndicatorProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { cn } from "../../../../shared/utils/cn.js";
|
|
4
|
+
import { motion } from "motion/react";
|
|
5
|
+
const LEVEL_LABELS = {
|
|
6
|
+
low: "Low",
|
|
7
|
+
normal: "Normal",
|
|
8
|
+
elevated: "Elevated",
|
|
9
|
+
high: "High",
|
|
10
|
+
spike: "Spike",
|
|
11
|
+
};
|
|
12
|
+
const LEVEL_STYLES = {
|
|
13
|
+
low: "bg-green-500/20 text-green-500",
|
|
14
|
+
normal: "bg-as-surface-tertiary text-as-secondary",
|
|
15
|
+
elevated: "bg-yellow-500/20 text-yellow-600",
|
|
16
|
+
high: "bg-orange-500/20 text-orange-500",
|
|
17
|
+
spike: "bg-red-500/20 text-red-500",
|
|
18
|
+
};
|
|
19
|
+
function formatGasPrice(gweiString) {
|
|
20
|
+
const gwei = parseFloat(gweiString);
|
|
21
|
+
if (gwei < 0.001)
|
|
22
|
+
return "<0.001";
|
|
23
|
+
if (gwei < 1)
|
|
24
|
+
return gwei.toFixed(3);
|
|
25
|
+
if (gwei < 10)
|
|
26
|
+
return gwei.toFixed(2);
|
|
27
|
+
return gwei.toFixed(1);
|
|
28
|
+
}
|
|
29
|
+
export function GasIndicator({ gasPrice, className }) {
|
|
30
|
+
return (_jsxs(motion.div, { initial: { opacity: 0, y: 10 }, animate: { opacity: 1, y: 0 }, transition: { duration: 0.2 }, className: cn("flex items-center justify-between rounded-lg px-3 py-2", gasPrice.isSpike ? "bg-yellow-500/10" : "bg-as-surface-secondary", className), children: [_jsx("div", { className: "flex items-center gap-2", children: _jsxs("span", { className: "text-as-secondary text-xs", children: ["Gas on ", gasPrice.chainName] }) }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: cn("rounded px-1.5 py-0.5 text-xs font-medium", LEVEL_STYLES[gasPrice.level]), children: LEVEL_LABELS[gasPrice.level] }), _jsxs("span", { className: "text-as-primary text-xs font-medium", children: [formatGasPrice(gasPrice.gasPriceGwei), " Gwei"] })] })] }));
|
|
31
|
+
}
|
|
@@ -7,6 +7,7 @@ export * from "./useAnyspendQuote";
|
|
|
7
7
|
export * from "./useAnyspendTokens";
|
|
8
8
|
export * from "./useCoinbaseOnrampOptions";
|
|
9
9
|
export * from "./useConnectedUserProfile";
|
|
10
|
+
export * from "./useGasPrice";
|
|
10
11
|
export * from "./useGeoOnrampOptions";
|
|
11
12
|
export * from "./useGetGeo";
|
|
12
13
|
export * from "./useHyperliquidTransfer";
|
|
@@ -7,6 +7,7 @@ export * from "./useAnyspendQuote.js";
|
|
|
7
7
|
export * from "./useAnyspendTokens.js";
|
|
8
8
|
export * from "./useCoinbaseOnrampOptions.js";
|
|
9
9
|
export * from "./useConnectedUserProfile.js";
|
|
10
|
+
export * from "./useGasPrice.js";
|
|
10
11
|
export * from "./useGeoOnrampOptions.js";
|
|
11
12
|
export * from "./useGetGeo.js";
|
|
12
13
|
export * from "./useHyperliquidTransfer.js";
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { GasPriceData } from "../../types/gas";
|
|
2
|
+
export interface UseGasPriceOptions {
|
|
3
|
+
/** Refetch interval in ms (default: 10000 = 10s) */
|
|
4
|
+
refetchInterval?: number;
|
|
5
|
+
/** Whether to enable the query (default: true if chainId is supported) */
|
|
6
|
+
enabled?: boolean;
|
|
7
|
+
}
|
|
8
|
+
export interface UseGasPriceResult {
|
|
9
|
+
/** Gas price data */
|
|
10
|
+
gasPrice: GasPriceData | undefined;
|
|
11
|
+
/** Whether the query is loading */
|
|
12
|
+
isLoading: boolean;
|
|
13
|
+
/** Whether there's an error */
|
|
14
|
+
isError: boolean;
|
|
15
|
+
/** Error object if any */
|
|
16
|
+
error: Error | null;
|
|
17
|
+
/** Whether gas is currently spiking */
|
|
18
|
+
isSpike: boolean;
|
|
19
|
+
/** Refetch function */
|
|
20
|
+
refetch: () => void;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* React hook to fetch current gas price for a chain.
|
|
24
|
+
*
|
|
25
|
+
* @param chainId - The chain ID to fetch gas price for
|
|
26
|
+
* @param options - Optional configuration
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```tsx
|
|
30
|
+
* const { gasPrice, isSpike, isLoading } = useGasPrice(8453); // Base
|
|
31
|
+
*
|
|
32
|
+
* if (isSpike) {
|
|
33
|
+
* return <Warning>Gas prices are high: {gasPrice?.recommendation}</Warning>;
|
|
34
|
+
* }
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
export declare function useGasPrice(chainId: number | undefined, options?: UseGasPriceOptions): UseGasPriceResult;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { useQuery } from "@tanstack/react-query";
|
|
2
|
+
import { useMemo } from "react";
|
|
3
|
+
import { gasService, isGasOracleSupported } from "../../services/gas.js";
|
|
4
|
+
/**
|
|
5
|
+
* React hook to fetch current gas price for a chain.
|
|
6
|
+
*
|
|
7
|
+
* @param chainId - The chain ID to fetch gas price for
|
|
8
|
+
* @param options - Optional configuration
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```tsx
|
|
12
|
+
* const { gasPrice, isSpike, isLoading } = useGasPrice(8453); // Base
|
|
13
|
+
*
|
|
14
|
+
* if (isSpike) {
|
|
15
|
+
* return <Warning>Gas prices are high: {gasPrice?.recommendation}</Warning>;
|
|
16
|
+
* }
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
export function useGasPrice(chainId, options = {}) {
|
|
20
|
+
const { refetchInterval = 10000, enabled } = options;
|
|
21
|
+
const isSupported = chainId !== undefined && isGasOracleSupported(chainId);
|
|
22
|
+
const queryEnabled = enabled ?? isSupported;
|
|
23
|
+
const { data, isLoading, isError, error, refetch } = useQuery({
|
|
24
|
+
queryKey: ["gasPrice", chainId],
|
|
25
|
+
queryFn: () => gasService.fetch(chainId),
|
|
26
|
+
enabled: queryEnabled && chainId !== undefined,
|
|
27
|
+
refetchInterval,
|
|
28
|
+
staleTime: 5000, // Consider data fresh for 5s
|
|
29
|
+
retry: 2,
|
|
30
|
+
refetchOnWindowFocus: true,
|
|
31
|
+
});
|
|
32
|
+
return useMemo(() => ({
|
|
33
|
+
gasPrice: data,
|
|
34
|
+
isLoading,
|
|
35
|
+
isError,
|
|
36
|
+
error: error,
|
|
37
|
+
isSpike: data?.isSpike ?? false,
|
|
38
|
+
refetch,
|
|
39
|
+
}), [data, isLoading, isError, error, refetch]);
|
|
40
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { GasOracleResponse, GasPriceData } from "../types/gas";
|
|
2
|
+
/** Supported chain IDs for gas oracle */
|
|
3
|
+
export declare const GAS_ORACLE_SUPPORTED_CHAINS: readonly [1, 137, 42161, 8453, 56, 10, 43114, 8333, 2741, 4689, 3338, 1329];
|
|
4
|
+
export type GasOracleSupportedChainId = (typeof GAS_ORACLE_SUPPORTED_CHAINS)[number];
|
|
5
|
+
/** Check if a chain is supported by the gas oracle */
|
|
6
|
+
export declare function isGasOracleSupported(chainId: number): chainId is GasOracleSupportedChainId;
|
|
7
|
+
/** Fetch gas price data from the gas oracle */
|
|
8
|
+
export declare function fetchGasPrice(chainId: number): Promise<GasOracleResponse>;
|
|
9
|
+
/** Transform raw oracle response to simplified UI data */
|
|
10
|
+
export declare function toGasPriceData(response: GasOracleResponse): GasPriceData;
|
|
11
|
+
/** Gas service for fetching and transforming gas data */
|
|
12
|
+
export declare const gasService: {
|
|
13
|
+
/** Fetch raw gas oracle response */
|
|
14
|
+
fetchRaw: typeof fetchGasPrice;
|
|
15
|
+
/** Fetch and transform to UI-friendly format */
|
|
16
|
+
fetch: (chainId: number) => Promise<GasPriceData>;
|
|
17
|
+
/** Check if chain is supported */
|
|
18
|
+
isSupported: typeof isGasOracleSupported;
|
|
19
|
+
/** List of supported chain IDs */
|
|
20
|
+
supportedChains: readonly [1, 137, 42161, 8453, 56, 10, 43114, 8333, 2741, 4689, 3338, 1329];
|
|
21
|
+
};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { GAS_ORACLE_BASE_URL } from "../constants/index.js";
|
|
2
|
+
/** Supported chain IDs for gas oracle */
|
|
3
|
+
export const GAS_ORACLE_SUPPORTED_CHAINS = [
|
|
4
|
+
1, // Ethereum
|
|
5
|
+
137, // Polygon
|
|
6
|
+
42161, // Arbitrum
|
|
7
|
+
8453, // Base
|
|
8
|
+
56, // BSC
|
|
9
|
+
10, // Optimism
|
|
10
|
+
43114, // Avalanche
|
|
11
|
+
8333, // B3
|
|
12
|
+
2741, // Abstract
|
|
13
|
+
4689, // IoTeX
|
|
14
|
+
3338, // Peaq
|
|
15
|
+
1329, // Sei
|
|
16
|
+
];
|
|
17
|
+
/** Check if a chain is supported by the gas oracle */
|
|
18
|
+
export function isGasOracleSupported(chainId) {
|
|
19
|
+
return GAS_ORACLE_SUPPORTED_CHAINS.includes(chainId);
|
|
20
|
+
}
|
|
21
|
+
/** Fetch gas price data from the gas oracle */
|
|
22
|
+
export async function fetchGasPrice(chainId) {
|
|
23
|
+
const response = await fetch(`${GAS_ORACLE_BASE_URL}/gas/${chainId}`);
|
|
24
|
+
if (!response.ok) {
|
|
25
|
+
throw new Error(`Failed to fetch gas price for chain ${chainId}: ${response.status}`);
|
|
26
|
+
}
|
|
27
|
+
return response.json();
|
|
28
|
+
}
|
|
29
|
+
/** Transform raw oracle response to simplified UI data */
|
|
30
|
+
export function toGasPriceData(response) {
|
|
31
|
+
const { analysis } = response;
|
|
32
|
+
const isSpike = analysis.level === "elevated" || analysis.level === "high" || analysis.level === "spike";
|
|
33
|
+
return {
|
|
34
|
+
chainId: response.chainId,
|
|
35
|
+
chainName: response.chainName,
|
|
36
|
+
gasPriceGwei: response.gasPrice.standard,
|
|
37
|
+
baseFeeGwei: response.eip1559?.baseFee,
|
|
38
|
+
level: analysis.level,
|
|
39
|
+
isSpike,
|
|
40
|
+
recommendation: analysis.recommendation,
|
|
41
|
+
vsMedian: analysis.vs1h, // Use 1h as primary comparison
|
|
42
|
+
source: response.source,
|
|
43
|
+
timestamp: response.timestamp,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
/** Gas service for fetching and transforming gas data */
|
|
47
|
+
export const gasService = {
|
|
48
|
+
/** Fetch raw gas oracle response */
|
|
49
|
+
fetchRaw: fetchGasPrice,
|
|
50
|
+
/** Fetch and transform to UI-friendly format */
|
|
51
|
+
fetch: async (chainId) => {
|
|
52
|
+
const response = await fetchGasPrice(chainId);
|
|
53
|
+
return toGasPriceData(response);
|
|
54
|
+
},
|
|
55
|
+
/** Check if chain is supported */
|
|
56
|
+
isSupported: isGasOracleSupported,
|
|
57
|
+
/** List of supported chain IDs */
|
|
58
|
+
supportedChains: GAS_ORACLE_SUPPORTED_CHAINS,
|
|
59
|
+
};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/** Gas level classification based on historical percentiles */
|
|
2
|
+
export type GasLevel = "low" | "normal" | "elevated" | "high" | "spike";
|
|
3
|
+
/** Gas prices in Gwei for different speed tiers */
|
|
4
|
+
export interface GasPrices {
|
|
5
|
+
low: string;
|
|
6
|
+
standard: string;
|
|
7
|
+
fast: string;
|
|
8
|
+
instant?: string;
|
|
9
|
+
}
|
|
10
|
+
/** EIP-1559 specific gas data */
|
|
11
|
+
export interface Eip1559Data {
|
|
12
|
+
baseFee: string;
|
|
13
|
+
maxPriorityFee: GasPrices;
|
|
14
|
+
maxFee: GasPrices;
|
|
15
|
+
}
|
|
16
|
+
/** Spike analysis comparing current gas to historical data */
|
|
17
|
+
export interface GasSpikeAnalysis {
|
|
18
|
+
level: GasLevel;
|
|
19
|
+
percentile: number;
|
|
20
|
+
/** Ratio to 1h median (primary spike detection) */
|
|
21
|
+
vs1h: number;
|
|
22
|
+
/** Ratio to 4h median (short-term context) */
|
|
23
|
+
vs4h: number;
|
|
24
|
+
/** Ratio to 24h median (daily context) */
|
|
25
|
+
vs24h: number;
|
|
26
|
+
recommendation: string;
|
|
27
|
+
}
|
|
28
|
+
/** Full gas response from the gas oracle */
|
|
29
|
+
export interface GasOracleResponse {
|
|
30
|
+
chainId: number;
|
|
31
|
+
chainName: string;
|
|
32
|
+
timestamp: string;
|
|
33
|
+
blockNumber?: number;
|
|
34
|
+
source: "blocknative" | "rpc";
|
|
35
|
+
gasPrice: GasPrices;
|
|
36
|
+
eip1559?: Eip1559Data;
|
|
37
|
+
analysis: GasSpikeAnalysis;
|
|
38
|
+
cached: boolean;
|
|
39
|
+
cacheAge?: number;
|
|
40
|
+
}
|
|
41
|
+
/** Simplified gas data for UI display */
|
|
42
|
+
export interface GasPriceData {
|
|
43
|
+
chainId: number;
|
|
44
|
+
chainName: string;
|
|
45
|
+
/** Standard gas price in Gwei */
|
|
46
|
+
gasPriceGwei: string;
|
|
47
|
+
/** Base fee in Gwei (EIP-1559 chains) */
|
|
48
|
+
baseFeeGwei?: string;
|
|
49
|
+
/** Gas level classification */
|
|
50
|
+
level: GasLevel;
|
|
51
|
+
/** Whether gas is currently spiking (elevated, high, or spike) */
|
|
52
|
+
isSpike: boolean;
|
|
53
|
+
/** Human-readable recommendation */
|
|
54
|
+
recommendation: string;
|
|
55
|
+
/** Ratio to recent median (1 = normal, >1.5 = elevated) */
|
|
56
|
+
vsMedian: number;
|
|
57
|
+
/** Data source */
|
|
58
|
+
source: "blocknative" | "rpc";
|
|
59
|
+
/** Timestamp of the data */
|
|
60
|
+
timestamp: string;
|
|
61
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -22,7 +22,7 @@ export function B3Provider({ theme = "light", children, accountOverride, environ
|
|
|
22
22
|
// deprecated since v0.0.87
|
|
23
23
|
toaster: _toaster, clientType = "rest", rpcUrls, partnerId, stripePublishableKey, onConnect, connectors, overrideDefaultConnectors = false, createClientReferenceId, enableTurnkey = false, defaultPermissions, }) {
|
|
24
24
|
const setConfig = useB3ConfigStore(state => state.setConfig);
|
|
25
|
-
// Initialize config store on mount
|
|
25
|
+
// Initialize config store on mount
|
|
26
26
|
useEffect(() => {
|
|
27
27
|
setConfig({
|
|
28
28
|
accountOverride,
|
|
@@ -36,7 +36,19 @@ toaster: _toaster, clientType = "rest", rpcUrls, partnerId, stripePublishableKey
|
|
|
36
36
|
enableTurnkey,
|
|
37
37
|
defaultPermissions,
|
|
38
38
|
});
|
|
39
|
-
}, [
|
|
39
|
+
}, [
|
|
40
|
+
accountOverride,
|
|
41
|
+
environment,
|
|
42
|
+
automaticallySetFirstEoa,
|
|
43
|
+
theme,
|
|
44
|
+
clientType,
|
|
45
|
+
partnerId,
|
|
46
|
+
stripePublishableKey,
|
|
47
|
+
createClientReferenceId,
|
|
48
|
+
enableTurnkey,
|
|
49
|
+
defaultPermissions,
|
|
50
|
+
setConfig,
|
|
51
|
+
]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
40
52
|
// Initialize Google Analytics on mount
|
|
41
53
|
useEffect(() => {
|
|
42
54
|
loadGA4Script();
|
|
@@ -4,8 +4,8 @@ import { useQuery } from "@tanstack/react-query";
|
|
|
4
4
|
import { viemToThirdwebChain } from "../../../shared/constants/chains/b3Chain.js";
|
|
5
5
|
import { getChainById } from "../../../shared/utils/chains.js";
|
|
6
6
|
import { client } from "../../../shared/utils/thirdweb.js";
|
|
7
|
-
import { getWalletBalance } from "thirdweb/wallets";
|
|
8
7
|
import invariant from "invariant";
|
|
8
|
+
import { getWalletBalance } from "thirdweb/wallets";
|
|
9
9
|
export function useTokenBalancesByChain({ address, chainsIds, enabled = true, }) {
|
|
10
10
|
// Normalize chains to array
|
|
11
11
|
const chainIds = Array.isArray(chainsIds) ? chainsIds : [chainsIds];
|
|
@@ -46,7 +46,9 @@ export function useTokenBalancesByChain({ address, chainsIds, enabled = true, })
|
|
|
46
46
|
staleTime: 30000, // Consider data fresh for 30 seconds
|
|
47
47
|
gcTime: 5 * 60 * 1000, // Keep in cache for 5 minutes
|
|
48
48
|
retry: 2, // Limit retries on failure
|
|
49
|
-
|
|
49
|
+
// Enable structural sharing to prevent infinite loops
|
|
50
|
+
// This ensures we only get new references when data actually changes
|
|
51
|
+
structuralSharing: true,
|
|
50
52
|
});
|
|
51
53
|
return {
|
|
52
54
|
nativeTokens: combinedData?.nativeTokens ?? [],
|