Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Web vitals #250

Draft
wants to merge 4 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions app/Analytics.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
'use client';

import { usePathname } from 'lib/i18n/navigation';
import { init, track } from 'lib/utils/analytics';
import { analytics } from 'lib/utils/analytics';
import Script from 'next/script';
import { useEffect } from 'react';

const Analytics = () => {
const path = usePathname();

useEffect(() => {
init();
analytics.init();
}, []);

useEffect(() => {
if (!path) return;
track('Viewed Page', { path });
analytics.track('Viewed Page', { path });
}, [path]);

// SimpleAnalytics
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import TipSection from 'components/common/donate/TipSection';
import { useDonate } from 'lib/hooks/ethereum/useDonate';
import { useAddressPageContext } from 'lib/hooks/page-context/AddressPageContext';
import { TokenAllowanceData } from 'lib/utils/allowances';
import { track } from 'lib/utils/analytics';
import { analytics } from 'lib/utils/analytics';
import { useTranslations } from 'next-intl';
import { useState } from 'react';
import ControlsWrapper from '../ControlsWrapper';
Expand Down Expand Up @@ -33,7 +33,7 @@ const BatchRevokeControls = ({ selectedAllowances, isRevoking, isAllConfirmed, s
return 'mid';
};

track('Batch Revoked', {
analytics.track('Batch Revoked', {
chainId: selectedChainId,
address,
allowances: selectedAllowances.length,
Expand Down
6 changes: 3 additions & 3 deletions components/common/Logo.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use client';

import Image from 'next/image';
import { useState } from 'react';
import { memo, useState } from 'react';
import { twMerge } from 'tailwind-merge';
import PlaceholderIcon from './PlaceholderIcon';

Expand All @@ -14,7 +14,7 @@ interface Props {
className?: string;
}

const Logo = ({ src, alt, size, square, border, className }: Props) => {
const Logo = memo(({ src, alt, size, square, border, className }: Props) => {
const [error, setError] = useState(false);

if (error || !src) {
Expand Down Expand Up @@ -52,6 +52,6 @@ const Logo = ({ src, alt, size, square, border, className }: Props) => {
onError={() => setError(true)}
/>
);
};
});

export default Logo;
4 changes: 2 additions & 2 deletions components/common/donate/DonateButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { useState } from 'react';
import DonateModal, { DonateButtonType } from './DonateModal';

interface Props {
size: 'sm' | 'md' | 'lg' | 'none' | 'menu';
size?: 'sm' | 'md' | 'lg' | 'none' | 'menu';
style?: 'primary' | 'secondary' | 'tertiary' | 'none';
className?: string;
type: DonateButtonType;
Expand All @@ -30,7 +30,7 @@ const DonateButton = ({ size, style, className, type }: Props) => {
{t('common.buttons.donate')}
</Button>

<DonateModal open={open} setOpen={(open) => (open ? handleOpen() : handleClose())} type={type} />
{open && <DonateModal open={open} setOpen={(open) => (open ? handleOpen() : handleClose())} type={type} />}
</>
);
};
Expand Down
4 changes: 2 additions & 2 deletions components/exploits/ExploitChecker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
} from 'lib/hooks/page-context/AddressPageContext';
import { isNullish } from 'lib/utils';
import { getAllowanceKey } from 'lib/utils/allowances';
import { track } from 'lib/utils/analytics';
import { analytics } from 'lib/utils/analytics';
import { getEventKey } from 'lib/utils/events';
import { Exploit, getExploitStatus } from 'lib/utils/exploits';
import ExploitStatus from './ExploitStatus';
Expand All @@ -29,7 +29,7 @@ const ExploitChecker = ({ exploit }: Props) => {
queryKey: ['exploit-status', exploit.slug, allowances?.map(getAllowanceKey), events?.map(getEventKey)],
queryFn: () => {
const status = getExploitStatus(events!, allowances!, exploit);
track('Exploit Checked', { exploit: exploit.slug, account: address, chainId: selectedChainId, status });
analytics.track('Exploit Checked', { exploit: exploit.slug, account: address, chainId: selectedChainId, status });
return status;
},
enabled: !isNullish(address) && !isNullish(events) && !isNullish(allowances) && !isNullish(selectedChainId),
Expand Down
4 changes: 2 additions & 2 deletions components/footer/ColorThemeSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { ComputerDesktopIcon, MoonIcon, SunIcon } from '@heroicons/react/24/outl
import Select from 'components/common/select/Select';
import { useColorTheme } from 'lib/hooks/useColorTheme';
import { useMounted } from 'lib/hooks/useMounted';
import { track } from 'lib/utils/analytics';
import { analytics } from 'lib/utils/analytics';
import { useTranslations } from 'next-intl';

const ColorThemeSelect = () => {
Expand All @@ -19,7 +19,7 @@ const ColorThemeSelect = () => {
] as const;

const selectTheme = (option: (typeof options)[number]) => {
track('Changed Color Theme', { theme: option.value });
analytics.track('Changed Color Theme', { theme: option.value });
setTheme(option.value);
};

Expand Down
4 changes: 2 additions & 2 deletions components/footer/LanguageSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import Select from 'components/common/select/Select';
import { Locale } from 'lib/i18n/config';
import { useCsrRouter } from 'lib/i18n/csr-navigation';
import { usePathname } from 'lib/i18n/navigation';
import { track } from 'lib/utils/analytics';
import { analytics } from 'lib/utils/analytics';
import { useLocale } from 'next-intl';
import { FormatOptionLabelMeta } from 'react-select';

Expand Down Expand Up @@ -36,7 +36,7 @@ const LanguageSelect = () => {

const selectLanguage = (option: Option) => {
const newLocale = option.value;
track('Changed language', { from: locale, to: newLocale });
analytics.track('Changed language', { from: locale, to: newLocale });
router.replace(path, { locale: newLocale, scroll: false, showProgress: false, retainSearchParams: ['chainId'] });
persistLocaleCookie(newLocale);
};
Expand Down
4 changes: 2 additions & 2 deletions components/signatures/cells/CancelMarketplaceCell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { useHandleTransaction } from 'lib/hooks/ethereum/useHandleTransaction';
import { useAddressPageContext } from 'lib/hooks/page-context/AddressPageContext';
import { Marketplace, OnCancel, TransactionSubmitted, TransactionType } from 'lib/interfaces';
import { waitForTransactionConfirmation } from 'lib/utils';
import { track } from 'lib/utils/analytics';
import { analytics } from 'lib/utils/analytics';
import { usePublicClient, useWalletClient } from 'wagmi';
import CancelCell from './CancelCell';

Expand All @@ -21,7 +21,7 @@ const CancelMarketplaceCell = ({ marketplace, onCancel }: Props) => {
const sendCancelTransaction = async (): Promise<TransactionSubmitted> => {
const hash = await marketplace?.cancelSignatures(walletClient!);

track('Cancelled Marketplace Signatures', {
analytics.track('Cancelled Marketplace Signatures', {
chainId: selectedChainId,
account: address,
marketplace: marketplace.name,
Expand Down
6 changes: 3 additions & 3 deletions lib/hooks/ethereum/EthereumProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import { useCsrRouter } from 'lib/i18n/csr-navigation';
import { usePathname } from 'lib/i18n/navigation';
import { createViemPublicClientForChain, getViemChainConfig, ORDERED_CHAINS } from 'lib/utils/chains';
import { ReactNode, useEffect } from 'react';
import { memo, ReactNode, useEffect } from 'react';
import { Chain } from 'viem';
import { createConfig, useAccount, useConnect, WagmiProvider } from 'wagmi';
import { coinbaseWallet, injected, safe, walletConnect } from 'wagmi/connectors';
Expand Down Expand Up @@ -47,7 +47,7 @@ export const EthereumProvider = ({ children }: Props) => {
);
};

const EthereumProviderChild = ({ children }: Props) => {
const EthereumProviderChild = memo(({ children }: Props) => {
const { connectAsync, connectors } = useConnect();
const { connector } = useAccount();
const router = useCsrRouter();
Expand Down Expand Up @@ -88,4 +88,4 @@ const EthereumProviderChild = ({ children }: Props) => {
.catch(console.error);
}, [connectors, connector]);
return <>{children}</>;
};
});
4 changes: 2 additions & 2 deletions lib/hooks/ethereum/useDonate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { DonateButtonType } from 'components/common/donate/DonateModal';
import { DONATION_ADDRESS } from 'lib/constants';
import { TransactionSubmitted, TransactionType } from 'lib/interfaces';
import { getWalletAddress, waitForTransactionConfirmation } from 'lib/utils';
import { track } from 'lib/utils/analytics';
import { analytics } from 'lib/utils/analytics';
import { getChainName, getChainNativeToken, getDefaultDonationAmount } from 'lib/utils/chains';
import { parseEther } from 'viem';
import { usePublicClient, useWalletClient } from 'wagmi';
Expand Down Expand Up @@ -39,7 +39,7 @@ export const useDonate = (chainId: number, type: DonateButtonType) => {
const transactionSubmitted = await handleTransaction(sendDonation(amount), TransactionType.DONATE);

if (transactionSubmitted) {
track('Donated', {
analytics.track('Donated', {
chainId,
chainName: getChainName(chainId),
nativeToken,
Expand Down
6 changes: 3 additions & 3 deletions lib/utils/allowances.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { type TransactionSubmitted, TransactionType } from 'lib/interfaces';
import { TransactionStore } from 'lib/stores/transaction-store';
import { type Address, formatUnits, type PublicClient, type WalletClient, type WriteContractParameters } from 'viem';
import { deduplicateArray, isNullish, waitForTransactionConfirmation, writeContractUnlessExcessiveGas } from '.';
import { track } from './analytics';
import { analytics } from './analytics';
import { isNetworkError, isRevertedError, isUserRejectionError, parseErrorMessage, stringifyError } from './errors';
import {
Erc20ApprovalEvent,
Expand Down Expand Up @@ -510,7 +510,7 @@ const trackTransaction = (allowance: TokenAllowanceData, hash: string, newAmount
if (!hash) return;

if (isErc721Contract(allowance.contract)) {
track('Revoked ERC721 allowance', {
analytics.track('Revoked ERC721 allowance', {
chainId: allowance.chainId,
account: allowance.owner,
spender: allowance.payload?.spender,
Expand All @@ -519,7 +519,7 @@ const trackTransaction = (allowance: TokenAllowanceData, hash: string, newAmount
});
}

track(newAmount === '0' ? 'Revoked ERC20 allowance' : 'Updated ERC20 allowance', {
analytics.track(newAmount === '0' ? 'Revoked ERC20 allowance' : 'Updated ERC20 allowance', {
chainId: allowance.chainId,
account: allowance.owner,
spender: allowance.payload?.spender,
Expand Down
27 changes: 18 additions & 9 deletions lib/utils/analytics.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,24 @@
import mixpanel from 'mixpanel-browser';

export const init = () => {
if (process.env.NEXT_PUBLIC_MIXPANEL_API_KEY) {
mixpanel.init(process.env.NEXT_PUBLIC_MIXPANEL_API_KEY, { ip: false });
}
};
export const analytics = {
isInitialized: false,
// init only when first used
init() {
if (this.isInitialized) return;
const apiKey = process.env.NEXT_PUBLIC_MIXPANEL_API_KEY;
if (apiKey && typeof window !== 'undefined') {
mixpanel.init(apiKey, { ip: false });
this.isInitialized = true;
}
},

export const track = (eventName: string, eventProperties: any) => {
if (typeof window === 'undefined') return;
track(eventName: string, eventProperties?: Record<string, any>) {
if (typeof window === 'undefined' || !process.env.NEXT_PUBLIC_MIXPANEL_API_KEY) return;
// lazy initialize if not already done
if (!this.isInitialized) {
this.init();
}

if (process.env.NEXT_PUBLIC_MIXPANEL_API_KEY) {
mixpanel.track(eventName, eventProperties);
}
},
};
3 changes: 3 additions & 0 deletions lib/utils/dynamic-import.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export function dynamicImport<T>(modulePath: string, exportName: keyof T) {
return () => import(modulePath).then((mod) => mod[exportName] as T[keyof T]);
}
4 changes: 2 additions & 2 deletions lib/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
WalletClient,
WriteContractParameters,
} from 'viem';
import { track } from './analytics';
import { analytics } from './analytics';
import type { Log, TokenEvent } from './events';

export const assertFulfilled = <T>(item: PromiseSettledResult<T>): item is PromiseFulfilledResult<T> => {
Expand Down Expand Up @@ -123,7 +123,7 @@ export const throwIfExcessiveGas = (chainId: number, address: Address, estimated

// Track excessive gas usage so we can blacklist tokens
// TODO: Use a different tool than analytics for this
track('Excessive gas limit', { chainId, address, estimatedGas: estimatedGas.toString() });
analytics.track('Excessive gas limit', { chainId, address, estimatedGas: estimatedGas.toString() });

throw new Error(
'This transaction has an excessive gas cost. It is most likely a spam token, so you do not need to revoke this approval.',
Expand Down
4 changes: 2 additions & 2 deletions lib/utils/risk.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ExclamationCircleIcon, ExclamationTriangleIcon, InformationCircleIcon } from '@heroicons/react/24/solid';
import { RiskFactor, RiskLevel } from 'lib/interfaces';
import { track } from './analytics';
import { analytics } from './analytics';

export const RiskFactorScore: Record<string, number> = {
blocklist: 100,
Expand All @@ -15,7 +15,7 @@ export const RiskFactorScore: Record<string, number> = {
export const filterUnknownRiskFactors = (riskFactors: RiskFactor[]): RiskFactor[] => {
return riskFactors.filter((riskFactor) => {
if (RiskFactorScore[riskFactor.type] === undefined) {
track('Unknown Risk Factor', riskFactor);
analytics.track('Unknown Risk Factor', riskFactor);
return false;
}

Expand Down
4 changes: 2 additions & 2 deletions lib/utils/tokens.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import ky from 'lib/ky';
import { getTokenPrice } from 'lib/price/utils';
import { Address, domainSeparator, getAbiItem, getAddress, pad, PublicClient, toHex, TypedDataDomain } from 'viem';
import { deduplicateArray } from '.';
import { track } from './analytics';
import { analytics } from './analytics';
import { isTransferTokenEvent, type TimeLog, type TokenEvent, TokenEventType } from './events';
import { formatFixedPointBigInt } from './formatting';
import { withFallback } from './promises';
Expand Down Expand Up @@ -335,7 +335,7 @@ export const getPermitDomain = async (contract: Erc20TokenContract): Promise<Typ

if (!domain) {
// If the domain separator is something else, we cannot generate a valid signature
track('Permit Domain Separator Mismatch', { name, verifyingContract, chainId });
analytics.track('Permit Domain Separator Mismatch', { name, verifyingContract, chainId });
throw new Error('Could not determine Permit Signature data');
}

Expand Down
Loading