@b3dotfun/sdk 0.0.30-alpha.6 → 0.0.30-alpha.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/global-account/react/components/B3Provider/B3Provider.js +5 -0
- package/dist/cjs/global-account/react/components/ManageAccount/ContentTokens.js +21 -0
- 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/useAnalytics.d.ts +7 -0
- package/dist/cjs/global-account/react/hooks/useAnalytics.js +29 -0
- package/dist/cjs/global-account/react/hooks/useSimBalance.js +5 -1
- package/dist/cjs/global-account/utils/analytics.d.ts +10 -0
- package/dist/cjs/global-account/utils/analytics.js +54 -0
- package/dist/esm/global-account/react/components/B3Provider/B3Provider.js +5 -0
- package/dist/esm/global-account/react/components/ManageAccount/ContentTokens.js +23 -2
- 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/useAnalytics.d.ts +7 -0
- package/dist/esm/global-account/react/hooks/useAnalytics.js +26 -0
- package/dist/esm/global-account/react/hooks/useSimBalance.js +5 -1
- package/dist/esm/global-account/utils/analytics.d.ts +10 -0
- package/dist/esm/global-account/utils/analytics.js +49 -0
- package/dist/types/global-account/react/hooks/index.d.ts +2 -1
- package/dist/types/global-account/react/hooks/useAnalytics.d.ts +7 -0
- package/dist/types/global-account/utils/analytics.d.ts +10 -0
- package/package.json +1 -1
- package/src/global-account/react/components/B3Provider/B3Provider.tsx +6 -0
- package/src/global-account/react/components/ManageAccount/ContentTokens.tsx +27 -1
- package/src/global-account/react/hooks/index.ts +2 -1
- package/src/global-account/react/hooks/useAnalytics.tsx +30 -0
- package/src/global-account/react/hooks/useSimBalance.ts +6 -3
- package/src/global-account/utils/analytics.ts +54 -0
- package/src/{anyspend/types → types}/window.d.ts +5 -1
|
@@ -5,6 +5,7 @@ exports.B3Provider = B3Provider;
|
|
|
5
5
|
exports.InnerProvider = InnerProvider;
|
|
6
6
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
7
7
|
const react_1 = require("../../../../global-account/react");
|
|
8
|
+
const analytics_1 = require("../../../../global-account/utils/analytics");
|
|
8
9
|
const supported_1 = require("../../../../shared/constants/chains/supported");
|
|
9
10
|
const react_query_1 = require("@tanstack/react-query");
|
|
10
11
|
const react_2 = require("react");
|
|
@@ -33,6 +34,10 @@ const queryClient = new react_query_1.QueryClient();
|
|
|
33
34
|
* Main B3Provider component
|
|
34
35
|
*/
|
|
35
36
|
function B3Provider({ theme = "light", children, accountOverride, environment, automaticallySetFirstEoa, simDuneApiKey, toaster, }) {
|
|
37
|
+
// Initialize Google Analytics on mount
|
|
38
|
+
(0, react_2.useEffect)(() => {
|
|
39
|
+
(0, analytics_1.loadGA4Script)();
|
|
40
|
+
}, []);
|
|
36
41
|
return ((0, jsx_runtime_1.jsx)(wagmi_1.WagmiProvider, { config: exports.wagmiConfig, children: (0, jsx_runtime_1.jsx)(react_query_1.QueryClientProvider, { client: queryClient, children: (0, jsx_runtime_1.jsx)(react_3.ThirdwebProvider, { children: (0, jsx_runtime_1.jsx)(react_1.TooltipProvider, { children: (0, jsx_runtime_1.jsx)(InnerProvider, { accountOverride: accountOverride, environment: environment, theme: theme, automaticallySetFirstEoa: !!automaticallySetFirstEoa, children: (0, jsx_runtime_1.jsxs)(react_1.RelayKitProviderWrapper, { simDuneApiKey: simDuneApiKey, children: [children, (0, jsx_runtime_1.jsx)(StyleRoot_1.StyleRoot, { id: "b3-root" }), (0, jsx_runtime_1.jsx)(sonner_1.Toaster, { theme: theme, position: toaster?.position, style: toaster?.style })] }) }) }) }) }) }));
|
|
37
42
|
}
|
|
38
43
|
/**
|
|
@@ -47,6 +47,8 @@ function ContentTokens({ activeTab }) {
|
|
|
47
47
|
const { data: simBalance, refetch: refetchSimBalance, isLoading: isLoadingBalance } = (0, react_1.useSimBalance)(account?.address);
|
|
48
48
|
// === BLOCKCHAIN INTERACTION ===
|
|
49
49
|
const { switchChainAndExecute } = (0, react_1.useUnifiedChainSwitchAndExecute)();
|
|
50
|
+
// === ANALYTICS ===
|
|
51
|
+
const { sendAnalyticsEvent } = (0, react_1.useAnalytics)();
|
|
50
52
|
// === ADDRESS VALIDATION ===
|
|
51
53
|
// Handle recipient address change with real-time validation using viem
|
|
52
54
|
const handleRecipientAddressChange = (value) => {
|
|
@@ -133,6 +135,13 @@ function ContentTokens({ activeTab }) {
|
|
|
133
135
|
}
|
|
134
136
|
setIsSending(true);
|
|
135
137
|
const amountInWei = (0, viem_1.parseUnits)(sendAmount, displayToken.decimals);
|
|
138
|
+
// Prepare analytics event data
|
|
139
|
+
const analyticsData = {
|
|
140
|
+
amount: sendAmount,
|
|
141
|
+
symbol: displayToken.symbol,
|
|
142
|
+
chain_id: displayToken.chain_id,
|
|
143
|
+
address: displayToken.address,
|
|
144
|
+
};
|
|
136
145
|
try {
|
|
137
146
|
const sendTokenData = (0, viem_1.encodeFunctionData)({
|
|
138
147
|
abi: viem_1.erc20Abi,
|
|
@@ -145,11 +154,23 @@ function ContentTokens({ activeTab }) {
|
|
|
145
154
|
value: displayToken.address === "native" ? amountInWei : BigInt(0),
|
|
146
155
|
});
|
|
147
156
|
if (tx) {
|
|
157
|
+
// Track successful send
|
|
158
|
+
sendAnalyticsEvent("send_token_button_click", {
|
|
159
|
+
...analyticsData,
|
|
160
|
+
success: true,
|
|
161
|
+
tx: (0, anyspend_1.getExplorerTxUrl)(displayToken.chain_id, tx),
|
|
162
|
+
});
|
|
148
163
|
// Reset form
|
|
149
164
|
setSendAmount("");
|
|
150
165
|
}
|
|
151
166
|
}
|
|
152
167
|
catch (error) {
|
|
168
|
+
// Track failed send
|
|
169
|
+
sendAnalyticsEvent("send_token_button_click", {
|
|
170
|
+
...analyticsData,
|
|
171
|
+
success: false,
|
|
172
|
+
reason: error.message || "Unknown error",
|
|
173
|
+
});
|
|
153
174
|
// Error
|
|
154
175
|
sonner_1.toast.error(`Failed to send ${displayToken.symbol}: ${error.message || "Unknown error"}`);
|
|
155
176
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export { useAccountAssets } from "./useAccountAssets";
|
|
2
2
|
export { useAccountWallet } from "./useAccountWallet";
|
|
3
3
|
export { useAddTWSessionKey } from "./useAddTWSessionKey";
|
|
4
|
+
export { useAnalytics } from "./useAnalytics";
|
|
4
5
|
export { useAuthentication } from "./useAuthentication";
|
|
5
6
|
export { useB3BalanceFromAddresses } from "./useB3BalanceFromAddresses";
|
|
6
7
|
export { useB3EnsName } from "./useB3EnsName";
|
|
@@ -18,13 +19,13 @@ export { useIsomorphicLayoutEffect } from "./useIsomorphicLayoutEffect";
|
|
|
18
19
|
export { useMediaQuery } from "./useMediaQuery";
|
|
19
20
|
export { useNativeBalance, useNativeBalanceFromRPC } from "./useNativeBalance";
|
|
20
21
|
export { useOneBalance } from "./useOneBalance";
|
|
21
|
-
export { useSimBalance } from "./useSimBalance";
|
|
22
22
|
export { useProfile, useProfilePreference, type CombinedProfile, type PreferenceRequestBody, type Profile, } from "./useProfile";
|
|
23
23
|
export { useQueryB3 } from "./useQueryB3";
|
|
24
24
|
export { useQueryBSMNT } from "./useQueryBSMNT";
|
|
25
25
|
export { useRemoveSessionKey } from "./useRemoveSessionKey";
|
|
26
26
|
export { useRouter } from "./useRouter";
|
|
27
27
|
export { useSearchParamsSSR } from "./useSearchParamsSSR";
|
|
28
|
+
export { useSimBalance } from "./useSimBalance";
|
|
28
29
|
export { useSiwe } from "./useSiwe";
|
|
29
30
|
export { useTokenBalance } from "./useTokenBalance";
|
|
30
31
|
export { useTokenBalancesByChain } from "./useTokenBalancesByChain";
|
|
@@ -14,13 +14,15 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
exports.useURLParams = exports.useUnifiedChainSwitchAndExecute = exports.useTokensFromAddress = exports.useTokenPriceWithFallback = exports.useTokenPrice = exports.useTokenFromUrl = exports.useTokenData = exports.useTokenBalancesByChain = exports.useTokenBalance = exports.useSiwe = exports.useSearchParamsSSR = exports.useRouter = exports.useRemoveSessionKey = exports.useQueryBSMNT = exports.useQueryB3 = exports.useProfilePreference = exports.useProfile = exports.
|
|
17
|
+
exports.useURLParams = exports.useUnifiedChainSwitchAndExecute = exports.useTokensFromAddress = exports.useTokenPriceWithFallback = exports.useTokenPrice = exports.useTokenFromUrl = exports.useTokenData = exports.useTokenBalancesByChain = exports.useTokenBalance = exports.useSiwe = exports.useSimBalance = exports.useSearchParamsSSR = exports.useRouter = exports.useRemoveSessionKey = exports.useQueryBSMNT = exports.useQueryB3 = exports.useProfilePreference = exports.useProfile = exports.useOneBalance = exports.useNativeBalanceFromRPC = exports.useNativeBalance = exports.useMediaQuery = exports.useIsomorphicLayoutEffect = exports.useIsMobile = exports.useHasMounted = exports.useHandleConnectWithPrivy = exports.useGetGeo = exports.useGetAllTWSigners = exports.useExchangeRate = exports.useConnect = exports.useChainSwitchWithAction = exports.useBestTransactionPath = exports.useB3EnsName = exports.useB3BalanceFromAddresses = exports.useAuthentication = exports.useAnalytics = exports.useAddTWSessionKey = exports.useAccountWallet = exports.useAccountAssets = void 0;
|
|
18
18
|
var useAccountAssets_1 = require("./useAccountAssets");
|
|
19
19
|
Object.defineProperty(exports, "useAccountAssets", { enumerable: true, get: function () { return useAccountAssets_1.useAccountAssets; } });
|
|
20
20
|
var useAccountWallet_1 = require("./useAccountWallet");
|
|
21
21
|
Object.defineProperty(exports, "useAccountWallet", { enumerable: true, get: function () { return useAccountWallet_1.useAccountWallet; } });
|
|
22
22
|
var useAddTWSessionKey_1 = require("./useAddTWSessionKey");
|
|
23
23
|
Object.defineProperty(exports, "useAddTWSessionKey", { enumerable: true, get: function () { return useAddTWSessionKey_1.useAddTWSessionKey; } });
|
|
24
|
+
var useAnalytics_1 = require("./useAnalytics");
|
|
25
|
+
Object.defineProperty(exports, "useAnalytics", { enumerable: true, get: function () { return useAnalytics_1.useAnalytics; } });
|
|
24
26
|
var useAuthentication_1 = require("./useAuthentication");
|
|
25
27
|
Object.defineProperty(exports, "useAuthentication", { enumerable: true, get: function () { return useAuthentication_1.useAuthentication; } });
|
|
26
28
|
var useB3BalanceFromAddresses_1 = require("./useB3BalanceFromAddresses");
|
|
@@ -55,8 +57,6 @@ Object.defineProperty(exports, "useNativeBalance", { enumerable: true, get: func
|
|
|
55
57
|
Object.defineProperty(exports, "useNativeBalanceFromRPC", { enumerable: true, get: function () { return useNativeBalance_1.useNativeBalanceFromRPC; } });
|
|
56
58
|
var useOneBalance_1 = require("./useOneBalance");
|
|
57
59
|
Object.defineProperty(exports, "useOneBalance", { enumerable: true, get: function () { return useOneBalance_1.useOneBalance; } });
|
|
58
|
-
var useSimBalance_1 = require("./useSimBalance");
|
|
59
|
-
Object.defineProperty(exports, "useSimBalance", { enumerable: true, get: function () { return useSimBalance_1.useSimBalance; } });
|
|
60
60
|
var useProfile_1 = require("./useProfile");
|
|
61
61
|
Object.defineProperty(exports, "useProfile", { enumerable: true, get: function () { return useProfile_1.useProfile; } });
|
|
62
62
|
Object.defineProperty(exports, "useProfilePreference", { enumerable: true, get: function () { return useProfile_1.useProfilePreference; } });
|
|
@@ -70,6 +70,8 @@ var useRouter_1 = require("./useRouter");
|
|
|
70
70
|
Object.defineProperty(exports, "useRouter", { enumerable: true, get: function () { return useRouter_1.useRouter; } });
|
|
71
71
|
var useSearchParamsSSR_1 = require("./useSearchParamsSSR");
|
|
72
72
|
Object.defineProperty(exports, "useSearchParamsSSR", { enumerable: true, get: function () { return useSearchParamsSSR_1.useSearchParamsSSR; } });
|
|
73
|
+
var useSimBalance_1 = require("./useSimBalance");
|
|
74
|
+
Object.defineProperty(exports, "useSimBalance", { enumerable: true, get: function () { return useSimBalance_1.useSimBalance; } });
|
|
73
75
|
var useSiwe_1 = require("./useSiwe");
|
|
74
76
|
Object.defineProperty(exports, "useSiwe", { enumerable: true, get: function () { return useSiwe_1.useSiwe; } });
|
|
75
77
|
var useTokenBalance_1 = require("./useTokenBalance");
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useAnalytics = useAnalytics;
|
|
4
|
+
const analytics_1 = require("../../../global-account/utils/analytics");
|
|
5
|
+
const useAccountWallet_1 = require("./useAccountWallet");
|
|
6
|
+
/**
|
|
7
|
+
* Analytics hook that provides sendAnalyticsEvent function
|
|
8
|
+
* Automatically includes user address from useAccountWallet
|
|
9
|
+
*/
|
|
10
|
+
function useAnalytics() {
|
|
11
|
+
const { address } = (0, useAccountWallet_1.useAccountWallet)();
|
|
12
|
+
/**
|
|
13
|
+
* Sends an analytics event to Google Analytics 4
|
|
14
|
+
* @param eventName - The name of the event to track
|
|
15
|
+
* @param parameters - Additional parameters to include with the event
|
|
16
|
+
*/
|
|
17
|
+
const sendAnalyticsEvent = (eventName, parameters) => {
|
|
18
|
+
// Merge user address with custom parameters
|
|
19
|
+
const eventData = {
|
|
20
|
+
user_address: address,
|
|
21
|
+
...parameters,
|
|
22
|
+
};
|
|
23
|
+
// Send event to GA4 using utility function
|
|
24
|
+
(0, analytics_1.sendGA4Event)(eventName, eventData);
|
|
25
|
+
};
|
|
26
|
+
return {
|
|
27
|
+
sendAnalyticsEvent,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
@@ -5,7 +5,11 @@ const react_query_1 = require("@tanstack/react-query");
|
|
|
5
5
|
async function fetchSimBalance(address) {
|
|
6
6
|
if (!address)
|
|
7
7
|
throw new Error("Address is required");
|
|
8
|
-
|
|
8
|
+
let url = `https://simdune-api.sean-430.workers.dev/?url=https://api.sim.dune.com/v1/evm/balances/${address}?metadata=logo&chain_ids=mainnet`;
|
|
9
|
+
if (process.env.PUBLIC_LOCAL_KEY) {
|
|
10
|
+
url += `&localkey=${process.env.PUBLIC_LOCAL_KEY}`;
|
|
11
|
+
}
|
|
12
|
+
const response = await fetch(url);
|
|
9
13
|
if (!response.ok) {
|
|
10
14
|
throw new Error(`Failed to fetch balance: ${response.statusText}`);
|
|
11
15
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Load Google Analytics 4 script and initialize
|
|
3
|
+
*/
|
|
4
|
+
export declare const loadGA4Script: () => void;
|
|
5
|
+
/**
|
|
6
|
+
* Send an analytics event to Google Analytics 4
|
|
7
|
+
* @param eventName - The name of the event to track
|
|
8
|
+
* @param parameters - Additional parameters to include with the event
|
|
9
|
+
*/
|
|
10
|
+
export declare const sendGA4Event: (eventName: string, parameters?: Record<string, any>) => void;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.sendGA4Event = exports.loadGA4Script = void 0;
|
|
4
|
+
const GA4_MEASUREMENT_ID = "G-Z67JCLMNZE";
|
|
5
|
+
/**
|
|
6
|
+
* Initialize Google Analytics 4
|
|
7
|
+
*/
|
|
8
|
+
const initializeGA4 = () => {
|
|
9
|
+
// Only initialize in browser environment
|
|
10
|
+
if (typeof window === "undefined")
|
|
11
|
+
return;
|
|
12
|
+
// Create gtag function if it doesn't exist
|
|
13
|
+
if (!window.gtag) {
|
|
14
|
+
window.dataLayer = window.dataLayer || [];
|
|
15
|
+
window.gtag = function gtag() {
|
|
16
|
+
window.dataLayer.push(arguments);
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
// Configure GA4
|
|
20
|
+
window.gtag("js", new Date());
|
|
21
|
+
window.gtag("config", GA4_MEASUREMENT_ID, {
|
|
22
|
+
page_location: window.location.href,
|
|
23
|
+
page_hostname: window.location.hostname,
|
|
24
|
+
});
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Load Google Analytics 4 script and initialize
|
|
28
|
+
*/
|
|
29
|
+
const loadGA4Script = () => {
|
|
30
|
+
if (typeof window === "undefined")
|
|
31
|
+
return;
|
|
32
|
+
// Check if script is already loaded
|
|
33
|
+
if (document.querySelector(`script[src*="${GA4_MEASUREMENT_ID}"]`))
|
|
34
|
+
return;
|
|
35
|
+
const script = document.createElement("script");
|
|
36
|
+
script.async = true;
|
|
37
|
+
script.src = `https://www.googletagmanager.com/gtag/js?id=${GA4_MEASUREMENT_ID}`;
|
|
38
|
+
document.head.appendChild(script);
|
|
39
|
+
script.onload = initializeGA4;
|
|
40
|
+
};
|
|
41
|
+
exports.loadGA4Script = loadGA4Script;
|
|
42
|
+
/**
|
|
43
|
+
* Send an analytics event to Google Analytics 4
|
|
44
|
+
* @param eventName - The name of the event to track
|
|
45
|
+
* @param parameters - Additional parameters to include with the event
|
|
46
|
+
*/
|
|
47
|
+
const sendGA4Event = (eventName, parameters) => {
|
|
48
|
+
// Only send events in browser environment
|
|
49
|
+
if (typeof window === "undefined" || !window.gtag)
|
|
50
|
+
return;
|
|
51
|
+
// Send event to GA4
|
|
52
|
+
window.gtag("event", eventName, parameters || {});
|
|
53
|
+
};
|
|
54
|
+
exports.sendGA4Event = sendGA4Event;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { RelayKitProviderWrapper, TooltipProvider, useAuthStore } from "../../../../global-account/react/index.js";
|
|
3
|
+
import { loadGA4Script } from "../../../../global-account/utils/analytics.js";
|
|
3
4
|
import { supportedChains } from "../../../../shared/constants/chains/supported.js";
|
|
4
5
|
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
|
5
6
|
import { useCallback, useEffect, useState } from "react";
|
|
@@ -28,6 +29,10 @@ const queryClient = new QueryClient();
|
|
|
28
29
|
* Main B3Provider component
|
|
29
30
|
*/
|
|
30
31
|
export function B3Provider({ theme = "light", children, accountOverride, environment, automaticallySetFirstEoa, simDuneApiKey, toaster, }) {
|
|
32
|
+
// Initialize Google Analytics on mount
|
|
33
|
+
useEffect(() => {
|
|
34
|
+
loadGA4Script();
|
|
35
|
+
}, []);
|
|
31
36
|
return (_jsx(WagmiProvider, { config: wagmiConfig, children: _jsx(QueryClientProvider, { client: queryClient, children: _jsx(ThirdwebProvider, { children: _jsx(TooltipProvider, { children: _jsx(InnerProvider, { accountOverride: accountOverride, environment: environment, theme: theme, automaticallySetFirstEoa: !!automaticallySetFirstEoa, children: _jsxs(RelayKitProviderWrapper, { simDuneApiKey: simDuneApiKey, children: [children, _jsx(StyleRoot, { id: "b3-root" }), _jsx(Toaster, { theme: theme, position: toaster?.position, style: toaster?.style })] }) }) }) }) }) }));
|
|
32
37
|
}
|
|
33
38
|
/**
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import { ALL_CHAINS } from "../../../../anyspend/index.js";
|
|
2
|
+
import { ALL_CHAINS, getExplorerTxUrl } from "../../../../anyspend/index.js";
|
|
3
3
|
import { ChainTokenIcon } from "../../../../anyspend/react/components/common/ChainTokenIcon.js";
|
|
4
|
-
import { Button, TransitionPanel, useSimBalance, useUnifiedChainSwitchAndExecute, } from "../../../../global-account/react/index.js";
|
|
4
|
+
import { Button, TransitionPanel, useAnalytics, useSimBalance, useUnifiedChainSwitchAndExecute, } from "../../../../global-account/react/index.js";
|
|
5
5
|
import { formatDisplayNumber, formatTokenAmount } from "../../../../shared/utils/number.js";
|
|
6
6
|
import { ArrowLeft, CircleHelp, Copy, Loader2, Send } from "lucide-react";
|
|
7
7
|
import { useEffect, useMemo, useRef, useState } from "react";
|
|
@@ -44,6 +44,8 @@ export function ContentTokens({ activeTab }) {
|
|
|
44
44
|
const { data: simBalance, refetch: refetchSimBalance, isLoading: isLoadingBalance } = useSimBalance(account?.address);
|
|
45
45
|
// === BLOCKCHAIN INTERACTION ===
|
|
46
46
|
const { switchChainAndExecute } = useUnifiedChainSwitchAndExecute();
|
|
47
|
+
// === ANALYTICS ===
|
|
48
|
+
const { sendAnalyticsEvent } = useAnalytics();
|
|
47
49
|
// === ADDRESS VALIDATION ===
|
|
48
50
|
// Handle recipient address change with real-time validation using viem
|
|
49
51
|
const handleRecipientAddressChange = (value) => {
|
|
@@ -130,6 +132,13 @@ export function ContentTokens({ activeTab }) {
|
|
|
130
132
|
}
|
|
131
133
|
setIsSending(true);
|
|
132
134
|
const amountInWei = parseUnits(sendAmount, displayToken.decimals);
|
|
135
|
+
// Prepare analytics event data
|
|
136
|
+
const analyticsData = {
|
|
137
|
+
amount: sendAmount,
|
|
138
|
+
symbol: displayToken.symbol,
|
|
139
|
+
chain_id: displayToken.chain_id,
|
|
140
|
+
address: displayToken.address,
|
|
141
|
+
};
|
|
133
142
|
try {
|
|
134
143
|
const sendTokenData = encodeFunctionData({
|
|
135
144
|
abi: erc20Abi,
|
|
@@ -142,11 +151,23 @@ export function ContentTokens({ activeTab }) {
|
|
|
142
151
|
value: displayToken.address === "native" ? amountInWei : BigInt(0),
|
|
143
152
|
});
|
|
144
153
|
if (tx) {
|
|
154
|
+
// Track successful send
|
|
155
|
+
sendAnalyticsEvent("send_token_button_click", {
|
|
156
|
+
...analyticsData,
|
|
157
|
+
success: true,
|
|
158
|
+
tx: getExplorerTxUrl(displayToken.chain_id, tx),
|
|
159
|
+
});
|
|
145
160
|
// Reset form
|
|
146
161
|
setSendAmount("");
|
|
147
162
|
}
|
|
148
163
|
}
|
|
149
164
|
catch (error) {
|
|
165
|
+
// Track failed send
|
|
166
|
+
sendAnalyticsEvent("send_token_button_click", {
|
|
167
|
+
...analyticsData,
|
|
168
|
+
success: false,
|
|
169
|
+
reason: error.message || "Unknown error",
|
|
170
|
+
});
|
|
150
171
|
// Error
|
|
151
172
|
toast.error(`Failed to send ${displayToken.symbol}: ${error.message || "Unknown error"}`);
|
|
152
173
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export { useAccountAssets } from "./useAccountAssets";
|
|
2
2
|
export { useAccountWallet } from "./useAccountWallet";
|
|
3
3
|
export { useAddTWSessionKey } from "./useAddTWSessionKey";
|
|
4
|
+
export { useAnalytics } from "./useAnalytics";
|
|
4
5
|
export { useAuthentication } from "./useAuthentication";
|
|
5
6
|
export { useB3BalanceFromAddresses } from "./useB3BalanceFromAddresses";
|
|
6
7
|
export { useB3EnsName } from "./useB3EnsName";
|
|
@@ -18,13 +19,13 @@ export { useIsomorphicLayoutEffect } from "./useIsomorphicLayoutEffect";
|
|
|
18
19
|
export { useMediaQuery } from "./useMediaQuery";
|
|
19
20
|
export { useNativeBalance, useNativeBalanceFromRPC } from "./useNativeBalance";
|
|
20
21
|
export { useOneBalance } from "./useOneBalance";
|
|
21
|
-
export { useSimBalance } from "./useSimBalance";
|
|
22
22
|
export { useProfile, useProfilePreference, type CombinedProfile, type PreferenceRequestBody, type Profile, } from "./useProfile";
|
|
23
23
|
export { useQueryB3 } from "./useQueryB3";
|
|
24
24
|
export { useQueryBSMNT } from "./useQueryBSMNT";
|
|
25
25
|
export { useRemoveSessionKey } from "./useRemoveSessionKey";
|
|
26
26
|
export { useRouter } from "./useRouter";
|
|
27
27
|
export { useSearchParamsSSR } from "./useSearchParamsSSR";
|
|
28
|
+
export { useSimBalance } from "./useSimBalance";
|
|
28
29
|
export { useSiwe } from "./useSiwe";
|
|
29
30
|
export { useTokenBalance } from "./useTokenBalance";
|
|
30
31
|
export { useTokenBalancesByChain } from "./useTokenBalancesByChain";
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export { useAccountAssets } from "./useAccountAssets.js";
|
|
2
2
|
export { useAccountWallet } from "./useAccountWallet.js";
|
|
3
3
|
export { useAddTWSessionKey } from "./useAddTWSessionKey.js";
|
|
4
|
+
export { useAnalytics } from "./useAnalytics.js";
|
|
4
5
|
export { useAuthentication } from "./useAuthentication.js";
|
|
5
6
|
export { useB3BalanceFromAddresses } from "./useB3BalanceFromAddresses.js";
|
|
6
7
|
export { useB3EnsName } from "./useB3EnsName.js";
|
|
@@ -18,13 +19,13 @@ export { useIsomorphicLayoutEffect } from "./useIsomorphicLayoutEffect.js";
|
|
|
18
19
|
export { useMediaQuery } from "./useMediaQuery.js";
|
|
19
20
|
export { useNativeBalance, useNativeBalanceFromRPC } from "./useNativeBalance.js";
|
|
20
21
|
export { useOneBalance } from "./useOneBalance.js";
|
|
21
|
-
export { useSimBalance } from "./useSimBalance.js";
|
|
22
22
|
export { useProfile, useProfilePreference, } from "./useProfile.js";
|
|
23
23
|
export { useQueryB3 } from "./useQueryB3.js";
|
|
24
24
|
export { useQueryBSMNT } from "./useQueryBSMNT.js";
|
|
25
25
|
export { useRemoveSessionKey } from "./useRemoveSessionKey.js";
|
|
26
26
|
export { useRouter } from "./useRouter.js";
|
|
27
27
|
export { useSearchParamsSSR } from "./useSearchParamsSSR.js";
|
|
28
|
+
export { useSimBalance } from "./useSimBalance.js";
|
|
28
29
|
export { useSiwe } from "./useSiwe.js";
|
|
29
30
|
export { useTokenBalance } from "./useTokenBalance.js";
|
|
30
31
|
export { useTokenBalancesByChain } from "./useTokenBalancesByChain.js";
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { sendGA4Event } from "../../../global-account/utils/analytics.js";
|
|
2
|
+
import { useAccountWallet } from "./useAccountWallet.js";
|
|
3
|
+
/**
|
|
4
|
+
* Analytics hook that provides sendAnalyticsEvent function
|
|
5
|
+
* Automatically includes user address from useAccountWallet
|
|
6
|
+
*/
|
|
7
|
+
export function useAnalytics() {
|
|
8
|
+
const { address } = useAccountWallet();
|
|
9
|
+
/**
|
|
10
|
+
* Sends an analytics event to Google Analytics 4
|
|
11
|
+
* @param eventName - The name of the event to track
|
|
12
|
+
* @param parameters - Additional parameters to include with the event
|
|
13
|
+
*/
|
|
14
|
+
const sendAnalyticsEvent = (eventName, parameters) => {
|
|
15
|
+
// Merge user address with custom parameters
|
|
16
|
+
const eventData = {
|
|
17
|
+
user_address: address,
|
|
18
|
+
...parameters,
|
|
19
|
+
};
|
|
20
|
+
// Send event to GA4 using utility function
|
|
21
|
+
sendGA4Event(eventName, eventData);
|
|
22
|
+
};
|
|
23
|
+
return {
|
|
24
|
+
sendAnalyticsEvent,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
@@ -2,7 +2,11 @@ import { useQuery } from "@tanstack/react-query";
|
|
|
2
2
|
async function fetchSimBalance(address) {
|
|
3
3
|
if (!address)
|
|
4
4
|
throw new Error("Address is required");
|
|
5
|
-
|
|
5
|
+
let url = `https://simdune-api.sean-430.workers.dev/?url=https://api.sim.dune.com/v1/evm/balances/${address}?metadata=logo&chain_ids=mainnet`;
|
|
6
|
+
if (process.env.PUBLIC_LOCAL_KEY) {
|
|
7
|
+
url += `&localkey=${process.env.PUBLIC_LOCAL_KEY}`;
|
|
8
|
+
}
|
|
9
|
+
const response = await fetch(url);
|
|
6
10
|
if (!response.ok) {
|
|
7
11
|
throw new Error(`Failed to fetch balance: ${response.statusText}`);
|
|
8
12
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Load Google Analytics 4 script and initialize
|
|
3
|
+
*/
|
|
4
|
+
export declare const loadGA4Script: () => void;
|
|
5
|
+
/**
|
|
6
|
+
* Send an analytics event to Google Analytics 4
|
|
7
|
+
* @param eventName - The name of the event to track
|
|
8
|
+
* @param parameters - Additional parameters to include with the event
|
|
9
|
+
*/
|
|
10
|
+
export declare const sendGA4Event: (eventName: string, parameters?: Record<string, any>) => void;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
const GA4_MEASUREMENT_ID = "G-Z67JCLMNZE";
|
|
2
|
+
/**
|
|
3
|
+
* Initialize Google Analytics 4
|
|
4
|
+
*/
|
|
5
|
+
const initializeGA4 = () => {
|
|
6
|
+
// Only initialize in browser environment
|
|
7
|
+
if (typeof window === "undefined")
|
|
8
|
+
return;
|
|
9
|
+
// Create gtag function if it doesn't exist
|
|
10
|
+
if (!window.gtag) {
|
|
11
|
+
window.dataLayer = window.dataLayer || [];
|
|
12
|
+
window.gtag = function gtag() {
|
|
13
|
+
window.dataLayer.push(arguments);
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
// Configure GA4
|
|
17
|
+
window.gtag("js", new Date());
|
|
18
|
+
window.gtag("config", GA4_MEASUREMENT_ID, {
|
|
19
|
+
page_location: window.location.href,
|
|
20
|
+
page_hostname: window.location.hostname,
|
|
21
|
+
});
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Load Google Analytics 4 script and initialize
|
|
25
|
+
*/
|
|
26
|
+
export const loadGA4Script = () => {
|
|
27
|
+
if (typeof window === "undefined")
|
|
28
|
+
return;
|
|
29
|
+
// Check if script is already loaded
|
|
30
|
+
if (document.querySelector(`script[src*="${GA4_MEASUREMENT_ID}"]`))
|
|
31
|
+
return;
|
|
32
|
+
const script = document.createElement("script");
|
|
33
|
+
script.async = true;
|
|
34
|
+
script.src = `https://www.googletagmanager.com/gtag/js?id=${GA4_MEASUREMENT_ID}`;
|
|
35
|
+
document.head.appendChild(script);
|
|
36
|
+
script.onload = initializeGA4;
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* Send an analytics event to Google Analytics 4
|
|
40
|
+
* @param eventName - The name of the event to track
|
|
41
|
+
* @param parameters - Additional parameters to include with the event
|
|
42
|
+
*/
|
|
43
|
+
export const sendGA4Event = (eventName, parameters) => {
|
|
44
|
+
// Only send events in browser environment
|
|
45
|
+
if (typeof window === "undefined" || !window.gtag)
|
|
46
|
+
return;
|
|
47
|
+
// Send event to GA4
|
|
48
|
+
window.gtag("event", eventName, parameters || {});
|
|
49
|
+
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export { useAccountAssets } from "./useAccountAssets";
|
|
2
2
|
export { useAccountWallet } from "./useAccountWallet";
|
|
3
3
|
export { useAddTWSessionKey } from "./useAddTWSessionKey";
|
|
4
|
+
export { useAnalytics } from "./useAnalytics";
|
|
4
5
|
export { useAuthentication } from "./useAuthentication";
|
|
5
6
|
export { useB3BalanceFromAddresses } from "./useB3BalanceFromAddresses";
|
|
6
7
|
export { useB3EnsName } from "./useB3EnsName";
|
|
@@ -18,13 +19,13 @@ export { useIsomorphicLayoutEffect } from "./useIsomorphicLayoutEffect";
|
|
|
18
19
|
export { useMediaQuery } from "./useMediaQuery";
|
|
19
20
|
export { useNativeBalance, useNativeBalanceFromRPC } from "./useNativeBalance";
|
|
20
21
|
export { useOneBalance } from "./useOneBalance";
|
|
21
|
-
export { useSimBalance } from "./useSimBalance";
|
|
22
22
|
export { useProfile, useProfilePreference, type CombinedProfile, type PreferenceRequestBody, type Profile, } from "./useProfile";
|
|
23
23
|
export { useQueryB3 } from "./useQueryB3";
|
|
24
24
|
export { useQueryBSMNT } from "./useQueryBSMNT";
|
|
25
25
|
export { useRemoveSessionKey } from "./useRemoveSessionKey";
|
|
26
26
|
export { useRouter } from "./useRouter";
|
|
27
27
|
export { useSearchParamsSSR } from "./useSearchParamsSSR";
|
|
28
|
+
export { useSimBalance } from "./useSimBalance";
|
|
28
29
|
export { useSiwe } from "./useSiwe";
|
|
29
30
|
export { useTokenBalance } from "./useTokenBalance";
|
|
30
31
|
export { useTokenBalancesByChain } from "./useTokenBalancesByChain";
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Load Google Analytics 4 script and initialize
|
|
3
|
+
*/
|
|
4
|
+
export declare const loadGA4Script: () => void;
|
|
5
|
+
/**
|
|
6
|
+
* Send an analytics event to Google Analytics 4
|
|
7
|
+
* @param eventName - The name of the event to track
|
|
8
|
+
* @param parameters - Additional parameters to include with the event
|
|
9
|
+
*/
|
|
10
|
+
export declare const sendGA4Event: (eventName: string, parameters?: Record<string, any>) => void;
|
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { RelayKitProviderWrapper, TooltipProvider, useAuthStore } from "@b3dotfun/sdk/global-account/react";
|
|
2
2
|
import { User } from "@b3dotfun/sdk/global-account/types/b3-api.types";
|
|
3
3
|
import { PermissionsConfig } from "@b3dotfun/sdk/global-account/types/permissions";
|
|
4
|
+
import { loadGA4Script } from "@b3dotfun/sdk/global-account/utils/analytics";
|
|
4
5
|
import { supportedChains } from "@b3dotfun/sdk/shared/constants/chains/supported";
|
|
5
6
|
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
|
6
7
|
import { useCallback, useEffect, useState } from "react";
|
|
@@ -60,6 +61,11 @@ export function B3Provider({
|
|
|
60
61
|
style?: React.CSSProperties;
|
|
61
62
|
};
|
|
62
63
|
}) {
|
|
64
|
+
// Initialize Google Analytics on mount
|
|
65
|
+
useEffect(() => {
|
|
66
|
+
loadGA4Script();
|
|
67
|
+
}, []);
|
|
68
|
+
|
|
63
69
|
return (
|
|
64
70
|
<WagmiProvider config={wagmiConfig}>
|
|
65
71
|
<QueryClientProvider client={queryClient}>
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { ALL_CHAINS } from "@b3dotfun/sdk/anyspend";
|
|
1
|
+
import { ALL_CHAINS, getExplorerTxUrl } from "@b3dotfun/sdk/anyspend";
|
|
2
2
|
import { ChainTokenIcon } from "@b3dotfun/sdk/anyspend/react/components/common/ChainTokenIcon";
|
|
3
3
|
import {
|
|
4
4
|
Button,
|
|
5
5
|
TransitionPanel,
|
|
6
|
+
useAnalytics,
|
|
6
7
|
useSimBalance,
|
|
7
8
|
useUnifiedChainSwitchAndExecute,
|
|
8
9
|
} from "@b3dotfun/sdk/global-account/react";
|
|
@@ -60,6 +61,9 @@ export function ContentTokens({ activeTab }: ContentTokensProps) {
|
|
|
60
61
|
// === BLOCKCHAIN INTERACTION ===
|
|
61
62
|
const { switchChainAndExecute } = useUnifiedChainSwitchAndExecute();
|
|
62
63
|
|
|
64
|
+
// === ANALYTICS ===
|
|
65
|
+
const { sendAnalyticsEvent } = useAnalytics();
|
|
66
|
+
|
|
63
67
|
// === ADDRESS VALIDATION ===
|
|
64
68
|
// Handle recipient address change with real-time validation using viem
|
|
65
69
|
const handleRecipientAddressChange = (value: string) => {
|
|
@@ -159,6 +163,14 @@ export function ContentTokens({ activeTab }: ContentTokensProps) {
|
|
|
159
163
|
|
|
160
164
|
const amountInWei = parseUnits(sendAmount, displayToken.decimals);
|
|
161
165
|
|
|
166
|
+
// Prepare analytics event data
|
|
167
|
+
const analyticsData = {
|
|
168
|
+
amount: sendAmount,
|
|
169
|
+
symbol: displayToken.symbol,
|
|
170
|
+
chain_id: displayToken.chain_id,
|
|
171
|
+
address: displayToken.address,
|
|
172
|
+
};
|
|
173
|
+
|
|
162
174
|
try {
|
|
163
175
|
const sendTokenData = encodeFunctionData({
|
|
164
176
|
abi: erc20Abi,
|
|
@@ -173,10 +185,24 @@ export function ContentTokens({ activeTab }: ContentTokensProps) {
|
|
|
173
185
|
});
|
|
174
186
|
|
|
175
187
|
if (tx) {
|
|
188
|
+
// Track successful send
|
|
189
|
+
sendAnalyticsEvent("send_token_button_click", {
|
|
190
|
+
...analyticsData,
|
|
191
|
+
success: true,
|
|
192
|
+
tx: getExplorerTxUrl(displayToken.chain_id, tx),
|
|
193
|
+
});
|
|
194
|
+
|
|
176
195
|
// Reset form
|
|
177
196
|
setSendAmount("");
|
|
178
197
|
}
|
|
179
198
|
} catch (error: any) {
|
|
199
|
+
// Track failed send
|
|
200
|
+
sendAnalyticsEvent("send_token_button_click", {
|
|
201
|
+
...analyticsData,
|
|
202
|
+
success: false,
|
|
203
|
+
reason: error.message || "Unknown error",
|
|
204
|
+
});
|
|
205
|
+
|
|
180
206
|
// Error
|
|
181
207
|
toast.error(`Failed to send ${displayToken.symbol}: ${error.message || "Unknown error"}`);
|
|
182
208
|
} finally {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export { useAccountAssets } from "./useAccountAssets";
|
|
2
2
|
export { useAccountWallet } from "./useAccountWallet";
|
|
3
3
|
export { useAddTWSessionKey } from "./useAddTWSessionKey";
|
|
4
|
+
export { useAnalytics } from "./useAnalytics";
|
|
4
5
|
export { useAuthentication } from "./useAuthentication";
|
|
5
6
|
export { useB3BalanceFromAddresses } from "./useB3BalanceFromAddresses";
|
|
6
7
|
export { useB3EnsName } from "./useB3EnsName";
|
|
@@ -18,7 +19,6 @@ export { useIsomorphicLayoutEffect } from "./useIsomorphicLayoutEffect";
|
|
|
18
19
|
export { useMediaQuery } from "./useMediaQuery";
|
|
19
20
|
export { useNativeBalance, useNativeBalanceFromRPC } from "./useNativeBalance";
|
|
20
21
|
export { useOneBalance } from "./useOneBalance";
|
|
21
|
-
export { useSimBalance } from "./useSimBalance";
|
|
22
22
|
export {
|
|
23
23
|
useProfile,
|
|
24
24
|
useProfilePreference,
|
|
@@ -31,6 +31,7 @@ export { useQueryBSMNT } from "./useQueryBSMNT";
|
|
|
31
31
|
export { useRemoveSessionKey } from "./useRemoveSessionKey";
|
|
32
32
|
export { useRouter } from "./useRouter";
|
|
33
33
|
export { useSearchParamsSSR } from "./useSearchParamsSSR";
|
|
34
|
+
export { useSimBalance } from "./useSimBalance";
|
|
34
35
|
export { useSiwe } from "./useSiwe";
|
|
35
36
|
export { useTokenBalance } from "./useTokenBalance";
|
|
36
37
|
export { useTokenBalancesByChain } from "./useTokenBalancesByChain";
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { sendGA4Event } from "@b3dotfun/sdk/global-account/utils/analytics";
|
|
2
|
+
import { useAccountWallet } from "./useAccountWallet";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Analytics hook that provides sendAnalyticsEvent function
|
|
6
|
+
* Automatically includes user address from useAccountWallet
|
|
7
|
+
*/
|
|
8
|
+
export function useAnalytics() {
|
|
9
|
+
const { address } = useAccountWallet();
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Sends an analytics event to Google Analytics 4
|
|
13
|
+
* @param eventName - The name of the event to track
|
|
14
|
+
* @param parameters - Additional parameters to include with the event
|
|
15
|
+
*/
|
|
16
|
+
const sendAnalyticsEvent = (eventName: string, parameters?: Record<string, any>) => {
|
|
17
|
+
// Merge user address with custom parameters
|
|
18
|
+
const eventData = {
|
|
19
|
+
user_address: address,
|
|
20
|
+
...parameters,
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
// Send event to GA4 using utility function
|
|
24
|
+
sendGA4Event(eventName, eventData);
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
return {
|
|
28
|
+
sendAnalyticsEvent,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
@@ -29,9 +29,12 @@ export interface SimBalanceResponse {
|
|
|
29
29
|
async function fetchSimBalance(address: string): Promise<SimBalanceResponse> {
|
|
30
30
|
if (!address) throw new Error("Address is required");
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
32
|
+
let url = `https://simdune-api.sean-430.workers.dev/?url=https://api.sim.dune.com/v1/evm/balances/${address}?metadata=logo&chain_ids=mainnet`;
|
|
33
|
+
if (process.env.PUBLIC_LOCAL_KEY) {
|
|
34
|
+
url += `&localkey=${process.env.PUBLIC_LOCAL_KEY}`;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const response = await fetch(url);
|
|
35
38
|
|
|
36
39
|
if (!response.ok) {
|
|
37
40
|
throw new Error(`Failed to fetch balance: ${response.statusText}`);
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
const GA4_MEASUREMENT_ID = "G-Z67JCLMNZE";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Initialize Google Analytics 4
|
|
5
|
+
*/
|
|
6
|
+
const initializeGA4 = () => {
|
|
7
|
+
// Only initialize in browser environment
|
|
8
|
+
if (typeof window === "undefined") return;
|
|
9
|
+
|
|
10
|
+
// Create gtag function if it doesn't exist
|
|
11
|
+
if (!window.gtag) {
|
|
12
|
+
window.dataLayer = window.dataLayer || [];
|
|
13
|
+
window.gtag = function gtag() {
|
|
14
|
+
window.dataLayer.push(arguments);
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Configure GA4
|
|
19
|
+
window.gtag("js", new Date());
|
|
20
|
+
window.gtag("config", GA4_MEASUREMENT_ID, {
|
|
21
|
+
page_location: window.location.href,
|
|
22
|
+
page_hostname: window.location.hostname,
|
|
23
|
+
});
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Load Google Analytics 4 script and initialize
|
|
28
|
+
*/
|
|
29
|
+
export const loadGA4Script = () => {
|
|
30
|
+
if (typeof window === "undefined") return;
|
|
31
|
+
|
|
32
|
+
// Check if script is already loaded
|
|
33
|
+
if (document.querySelector(`script[src*="${GA4_MEASUREMENT_ID}"]`)) return;
|
|
34
|
+
|
|
35
|
+
const script = document.createElement("script");
|
|
36
|
+
script.async = true;
|
|
37
|
+
script.src = `https://www.googletagmanager.com/gtag/js?id=${GA4_MEASUREMENT_ID}`;
|
|
38
|
+
document.head.appendChild(script);
|
|
39
|
+
|
|
40
|
+
script.onload = initializeGA4;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Send an analytics event to Google Analytics 4
|
|
45
|
+
* @param eventName - The name of the event to track
|
|
46
|
+
* @param parameters - Additional parameters to include with the event
|
|
47
|
+
*/
|
|
48
|
+
export const sendGA4Event = (eventName: string, parameters?: Record<string, any>) => {
|
|
49
|
+
// Only send events in browser environment
|
|
50
|
+
if (typeof window === "undefined" || !window.gtag) return;
|
|
51
|
+
|
|
52
|
+
// Send event to GA4
|
|
53
|
+
window.gtag("event", eventName, parameters || {});
|
|
54
|
+
};
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
// Global window interface augmentations for
|
|
1
|
+
// Global window interface augmentations for B3 SDK
|
|
2
2
|
|
|
3
3
|
declare global {
|
|
4
4
|
interface Window {
|
|
5
|
+
// AnySpend wallet providers
|
|
5
6
|
phantom?: {
|
|
6
7
|
solana?: {
|
|
7
8
|
isPhantom?: boolean;
|
|
@@ -9,6 +10,9 @@ declare global {
|
|
|
9
10
|
signAndSendTransaction: (transaction: any) => Promise<{ signature: string }>;
|
|
10
11
|
};
|
|
11
12
|
};
|
|
13
|
+
// Google Analytics 4
|
|
14
|
+
gtag: (...args: any[]) => void;
|
|
15
|
+
dataLayer: any[];
|
|
12
16
|
}
|
|
13
17
|
}
|
|
14
18
|
|