diff --git a/app/Analytics.tsx b/app/Analytics.tsx index 0f79e3cfe..a085ebd1d 100644 --- a/app/Analytics.tsx +++ b/app/Analytics.tsx @@ -1,7 +1,7 @@ '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'; @@ -9,12 +9,12 @@ const Analytics = () => { const path = usePathname(); useEffect(() => { - init(); + analytics.init(); }, []); useEffect(() => { if (!path) return; - track('Viewed Page', { path }); + analytics.track('Viewed Page', { path }); }, [path]); // SimpleAnalytics diff --git a/app/[locale]/learn/wallets/add-network/[slug]/page.tsx b/app/[locale]/learn/wallets/add-network/[slug]/page.tsx index 13a182363..4f331882c 100644 --- a/app/[locale]/learn/wallets/add-network/[slug]/page.tsx +++ b/app/[locale]/learn/wallets/add-network/[slug]/page.tsx @@ -77,7 +77,7 @@ const AddNewChainPage: NextPage = async ({ params }) => {

MetaMask Add Network 1 = async ({ params }) => {

MetaMask Add Network 2 { return (

- + {useMemo( + () => ( + + ), + [table], + )} { retainSearchParams?: boolean | string[]; } -const Button = ( +const ButtonComponent = ( { disabled, style, @@ -115,6 +115,7 @@ const Button = ( ); }; - // biome-ignore lint/suspicious/noExplicitAny: For some reason, forwardRef typing is not working here -export default forwardRef(Button as any); +const Button = memo(forwardRef(ButtonComponent as any)); + +export default Button; diff --git a/components/common/ChainLogo.tsx b/components/common/ChainLogo.tsx index c0268effe..0d860bed3 100644 --- a/components/common/ChainLogo.tsx +++ b/components/common/ChainLogo.tsx @@ -16,7 +16,7 @@ interface Props { checkMounted?: boolean; } -const ChainLogo = ({ chainId, size, tooltip, className, checkMounted }: Props) => { +const ChainLogo = memo(({ chainId, size, tooltip, className, checkMounted }: Props) => { const isMounted = useMounted(); const name = getChainName(chainId); const src = getChainLogo(chainId); @@ -37,6 +37,6 @@ const ChainLogo = ({ chainId, size, tooltip, className, checkMounted }: Props) = } return ; -}; +}); export default memo(ChainLogo); diff --git a/components/common/CopyButton.tsx b/components/common/CopyButton.tsx index 3a96e480f..02b4ca4e4 100644 --- a/components/common/CopyButton.tsx +++ b/components/common/CopyButton.tsx @@ -3,6 +3,7 @@ import { DocumentDuplicateIcon } from '@heroicons/react/24/outline'; import { writeToClipBoard } from 'lib/utils'; import { useTranslations } from 'next-intl'; +import { memo } from 'react'; import Button from './Button'; import WithHoverTooltip from './WithHoverTooltip'; @@ -12,11 +13,14 @@ interface Props { tooltip?: string; } -const CopyButton = ({ content, tooltip, className }: Props) => { +const CopyButtonComponent = ({ content, tooltip, className }: Props) => { const t = useTranslations(); + //Trying to stabilise onClick + const handleCopy = () => writeToClipBoard(content, t); + const button = ( - ); @@ -28,4 +32,7 @@ const CopyButton = ({ content, tooltip, className }: Props) => { return button; }; +// Memoize the component to avoid re-renders unless props change +const CopyButton = memo(CopyButtonComponent); + export default CopyButton; diff --git a/components/common/Logo.tsx b/components/common/Logo.tsx index fc309c1df..b46efde54 100644 --- a/components/common/Logo.tsx +++ b/components/common/Logo.tsx @@ -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'; @@ -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) { @@ -52,6 +52,6 @@ const Logo = ({ src, alt, size, square, border, className }: Props) => { onError={() => setError(true)} /> ); -}; +}); export default Logo; diff --git a/components/common/donate/DonateButton.tsx b/components/common/donate/DonateButton.tsx index 4e296cfb6..17371fdba 100644 --- a/components/common/donate/DonateButton.tsx +++ b/components/common/donate/DonateButton.tsx @@ -6,7 +6,7 @@ import { useState } from 'react'; import DonateModal, { type 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; @@ -30,7 +30,7 @@ const DonateButton = ({ size, style, className, type }: Props) => { {t('common.buttons.donate')} - (open ? handleOpen() : handleClose())} type={type} /> + {open && (open ? handleOpen() : handleClose())} type={type} />} ); }; diff --git a/components/common/donate/DonateButtonDropdown.tsx b/components/common/donate/DonateButtonDropdown.tsx index ed145a601..31880c393 100644 --- a/components/common/donate/DonateButtonDropdown.tsx +++ b/components/common/donate/DonateButtonDropdown.tsx @@ -1,7 +1,7 @@ 'use client'; import { useTranslations } from 'next-intl'; -import { useState } from 'react'; +import { memo, useState } from 'react'; import { DropdownMenuItem } from '../DropdownMenu'; import DonateModal from './DonateModal'; @@ -9,7 +9,7 @@ interface Props { className?: string; } -const DonateButtonDropdown = ({ className }: Props) => { +const DonateButtonDropdown = memo(({ className }: Props) => { const t = useTranslations(); const [open, setOpen] = useState(false); @@ -22,6 +22,6 @@ const DonateButtonDropdown = ({ className }: Props) => { ); -}; +}); export default DonateButtonDropdown; diff --git a/components/common/donate/DonateModal.tsx b/components/common/donate/DonateModal.tsx index e0bbd553f..254740946 100644 --- a/components/common/donate/DonateModal.tsx +++ b/components/common/donate/DonateModal.tsx @@ -5,7 +5,7 @@ import Button from 'components/common/Button'; import Modal from 'components/common/Modal'; import { useDonate } from 'lib/hooks/ethereum/useDonate'; import { useTranslations } from 'next-intl'; -import { useEffect, useState } from 'react'; +import { memo, useEffect, useState } from 'react'; import { useAsyncCallback } from 'react-async-hook'; import { useChainId } from 'wagmi'; import Input from '../Input'; @@ -18,7 +18,7 @@ interface Props { export type DonateButtonType = 'menu-button' | 'batch-revoke-tip'; -const DonateModal = ({ open, setOpen, type }: Props) => { +const DonateModal = memo(({ open, setOpen, type }: Props) => { const t = useTranslations(); const chainId = useChainId(); const { donate, nativeToken, defaultAmount } = useDonate(chainId, type); @@ -77,6 +77,6 @@ const DonateModal = ({ open, setOpen, type }: Props) => { ); -}; +}); export default DonateModal; diff --git a/components/exploits/ExploitChecker.tsx b/components/exploits/ExploitChecker.tsx index 0dc3f8888..705412323 100644 --- a/components/exploits/ExploitChecker.tsx +++ b/components/exploits/ExploitChecker.tsx @@ -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 { type Exploit, getExploitStatus } from 'lib/utils/exploits'; import ExploitStatus from './ExploitStatus'; @@ -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), diff --git a/components/footer/ColorThemeSelect.tsx b/components/footer/ColorThemeSelect.tsx index 1aab202b4..befbbbd5e 100644 --- a/components/footer/ColorThemeSelect.tsx +++ b/components/footer/ColorThemeSelect.tsx @@ -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 = () => { @@ -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); }; diff --git a/components/footer/LanguageSelect.tsx b/components/footer/LanguageSelect.tsx index e2363ea5a..032fbee42 100644 --- a/components/footer/LanguageSelect.tsx +++ b/components/footer/LanguageSelect.tsx @@ -5,7 +5,7 @@ import Select from 'components/common/select/Select'; import type { 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 type { FormatOptionLabelMeta } from 'react-select'; @@ -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); }; diff --git a/components/header/SearchBar.tsx b/components/header/SearchBar.tsx index f57f89e5c..840772670 100644 --- a/components/header/SearchBar.tsx +++ b/components/header/SearchBar.tsx @@ -4,7 +4,7 @@ import AddressSearchBox from 'components/common/AddressSearchBox'; import Button from 'components/common/Button'; import { useCsrRouter } from 'lib/i18n/csr-navigation'; import { useTranslations } from 'next-intl'; -import { useRef, useState } from 'react'; +import { useCallback, useRef, useState } from 'react'; import { twMerge } from 'tailwind-merge'; import { useAccount } from 'wagmi'; @@ -16,21 +16,21 @@ const SearchBar = () => { const { address } = useAccount(); const timerRef = useRef(); - const onFocus = () => { + const onFocus = useCallback(() => { clearTimeout(timerRef.current); setIsFocused(true); - }; + }, []); - const onBlur = () => { + const onBlur = useCallback(() => { timerRef.current = setTimeout(() => setIsFocused(false), 200); - }; + }, []); - const onClick = () => { + const onClick = useCallback(() => { if (address) { setValue(address); router.push(`/address/${address}`, { retainSearchParams: ['chainId'] }); } - }; + }, [address, router]); return (
diff --git a/components/landing/DemoVideo.tsx b/components/landing/DemoVideo.tsx index 67607dd2c..0413f2f80 100644 --- a/components/landing/DemoVideo.tsx +++ b/components/landing/DemoVideo.tsx @@ -6,8 +6,9 @@ const DemoVideo = () => { controls muted loop - preload="metadata" + preload="metadata" // Preload the video fully for faster LCP playsInline + poster="/assets/images/thumbnail/demo-thumbnail.webp" > diff --git a/components/signatures/cells/CancelMarketplaceCell.tsx b/components/signatures/cells/CancelMarketplaceCell.tsx index 2476d2ff1..347d427ee 100644 --- a/components/signatures/cells/CancelMarketplaceCell.tsx +++ b/components/signatures/cells/CancelMarketplaceCell.tsx @@ -3,7 +3,7 @@ import { useHandleTransaction } from 'lib/hooks/ethereum/useHandleTransaction'; import { useAddressPageContext } from 'lib/hooks/page-context/AddressPageContext'; import { type Marketplace, type OnCancel, type 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'; @@ -21,7 +21,7 @@ const CancelMarketplaceCell = ({ marketplace, onCancel }: Props) => { const sendCancelTransaction = async (): Promise => { const hash = await marketplace?.cancelSignatures(walletClient!); - track('Cancelled Marketplace Signatures', { + analytics.track('Cancelled Marketplace Signatures', { chainId: selectedChainId, account: address, marketplace: marketplace.name, diff --git a/components/signatures/cells/CancelPermitCell.tsx b/components/signatures/cells/CancelPermitCell.tsx index d6f808a22..643e02e2f 100644 --- a/components/signatures/cells/CancelPermitCell.tsx +++ b/components/signatures/cells/CancelPermitCell.tsx @@ -4,7 +4,7 @@ import { useHandleTransaction } from 'lib/hooks/ethereum/useHandleTransaction'; import { useAddressPageContext } from 'lib/hooks/page-context/AddressPageContext'; import { type OnCancel, type TransactionSubmitted, TransactionType } from 'lib/interfaces'; import { waitForTransactionConfirmation } from 'lib/utils'; -import { track } from 'lib/utils/analytics'; +import { analytics } from 'lib/utils/analytics'; import { permit } from 'lib/utils/permit'; import { type PermitTokenData, isErc721Contract } from 'lib/utils/tokens'; import { usePublicClient, useWalletClient } from 'wagmi'; @@ -25,7 +25,11 @@ const CancelPermitCell = ({ token, onCancel }: Props) => { if (isErc721Contract(token.contract)) return; const hash = await permit(walletClient!, token.contract, DUMMY_ADDRESS, 0n); - track('Cancelled Permit Signatures', { chainId: selectedChainId, account: address, token: token.contract.address }); + analytics.track('Cancelled Permit Signatures', { + chainId: selectedChainId, + account: address, + token: token.contract.address, + }); const waitForConfirmation = async () => { // TODO: Deduplicate this with the CancelMarketplaceCell diff --git a/lib/hooks/ethereum/EthereumProvider.tsx b/lib/hooks/ethereum/EthereumProvider.tsx index 5d8f0543d..cd1d60a3b 100644 --- a/lib/hooks/ethereum/EthereumProvider.tsx +++ b/lib/hooks/ethereum/EthereumProvider.tsx @@ -3,7 +3,7 @@ import { useCsrRouter } from 'lib/i18n/csr-navigation'; import { usePathname } from 'lib/i18n/navigation'; import { ORDERED_CHAINS, createViemPublicClientForChain, getViemChainConfig } from 'lib/utils/chains'; -import { type ReactNode, useEffect } from 'react'; +import { type ReactNode, memo, useEffect } from 'react'; import type { Chain } from 'viem'; import { WagmiProvider, createConfig, useAccount, useConnect } from 'wagmi'; import { coinbaseWallet, injected, safe, walletConnect } from 'wagmi/connectors'; @@ -48,7 +48,7 @@ export const EthereumProvider = ({ children }: Props) => { ); }; -const EthereumProviderChild = ({ children }: Props) => { +const EthereumProviderChild = memo(({ children }: Props) => { const { connectAsync, connectors } = useConnect(); const { connector, address } = useAccount(); const router = useCsrRouter(); @@ -103,7 +103,7 @@ const EthereumProviderChild = ({ children }: Props) => { }, [address]); return <>{children}; -}; +}); const isIframe = () => { return typeof window !== 'undefined' && window?.parent !== window; diff --git a/lib/hooks/ethereum/useAllowances.tsx b/lib/hooks/ethereum/useAllowances.tsx index a6498ce96..42d155063 100644 --- a/lib/hooks/ethereum/useAllowances.tsx +++ b/lib/hooks/ethereum/useAllowances.tsx @@ -9,7 +9,7 @@ import { getAllowancesFromEvents, stripAllowanceData, } from 'lib/utils/allowances'; -import { track } from 'lib/utils/analytics'; +import { analytics } from 'lib/utils/analytics'; import { type TimeLog, type TokenEvent, getEventKey } from 'lib/utils/events'; import { hasZeroBalance } from 'lib/utils/tokens'; import { useLayoutEffect, useState } from 'react'; @@ -30,7 +30,7 @@ export const useAllowances = (address: Address, events: TokenEvent[] | undefined queryKey: ['allowances', address, chainId, events?.map(getEventKey)], queryFn: async () => { const allowances = getAllowancesFromEvents(address, events!, publicClient, chainId); - track('Fetched Allowances', { account: address, chainId }); + analytics.track('Fetched Allowances', { account: address, chainId }); return allowances; }, // If events (transfers + approvals) don't change, derived allowances also shouldn't change, even if allowances diff --git a/lib/hooks/ethereum/useDonate.tsx b/lib/hooks/ethereum/useDonate.tsx index 11b1c57b2..5389fad75 100644 --- a/lib/hooks/ethereum/useDonate.tsx +++ b/lib/hooks/ethereum/useDonate.tsx @@ -4,7 +4,7 @@ import type { DonateButtonType } from 'components/common/donate/DonateModal'; import { DONATION_ADDRESS } from 'lib/constants'; import { type 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'; @@ -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, diff --git a/lib/utils/allowances.ts b/lib/utils/allowances.ts index 2a9cb5542..10a144e33 100644 --- a/lib/utils/allowances.ts +++ b/lib/utils/allowances.ts @@ -6,7 +6,7 @@ import { type TransactionSubmitted, TransactionType } from 'lib/interfaces'; import type { TransactionStore } from 'lib/stores/transaction-store'; import { type Address, type PublicClient, type WalletClient, type WriteContractParameters, formatUnits } 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 { type Erc20ApprovalEvent, @@ -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, @@ -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, diff --git a/lib/utils/analytics.ts b/lib/utils/analytics.ts index 4cc8faf2e..783aa94e2 100644 --- a/lib/utils/analytics.ts +++ b/lib/utils/analytics.ts @@ -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) { + 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); - } + }, }; diff --git a/lib/utils/index.ts b/lib/utils/index.ts index cd4352a01..da76d1883 100644 --- a/lib/utils/index.ts +++ b/lib/utils/index.ts @@ -16,7 +16,7 @@ import { pad, slice, } from 'viem'; -import { track } from './analytics'; +import { analytics } from './analytics'; import type { Log, TokenEvent } from './events'; export const assertFulfilled = (item: PromiseSettledResult): item is PromiseFulfilledResult => { @@ -124,7 +124,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.', diff --git a/lib/utils/risk.tsx b/lib/utils/risk.tsx index 79d675a8c..9ae5b6fbc 100644 --- a/lib/utils/risk.tsx +++ b/lib/utils/risk.tsx @@ -1,6 +1,6 @@ import { ExclamationCircleIcon, ExclamationTriangleIcon, InformationCircleIcon } from '@heroicons/react/24/solid'; import type { RiskFactor, RiskLevel } from 'lib/interfaces'; -import { track } from './analytics'; +import { analytics } from './analytics'; export const RiskFactorScore: Record = { blocklist: 100, @@ -16,7 +16,7 @@ export const RiskFactorScore: Record = { 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; } diff --git a/lib/utils/tokens.ts b/lib/utils/tokens.ts index 522708d9c..fb842b44b 100644 --- a/lib/utils/tokens.ts +++ b/lib/utils/tokens.ts @@ -14,7 +14,7 @@ import { toHex, } from 'viem'; import { deduplicateArray } from '.'; -import { track } from './analytics'; +import { analytics } from './analytics'; import { type TimeLog, type TokenEvent, TokenEventType, isTransferTokenEvent } from './events'; import { formatFixedPointBigInt } from './formatting'; import { withFallback } from './promises'; @@ -344,7 +344,7 @@ export const getPermitDomain = async (contract: Erc20TokenContract): Promise // NOTE: This file should not be edited -// see https://nextjs.org/docs/basic-features/typescript for more information. +// see https://nextjs.org/docs/app/building-your-application/configuring/typescript for more information. diff --git a/public/assets/images/learn/wallets/add-network/cover.jpg b/public/assets/images/learn/wallets/add-network/cover.jpg deleted file mode 100644 index 114eef513..000000000 Binary files a/public/assets/images/learn/wallets/add-network/cover.jpg and /dev/null differ diff --git a/public/assets/images/learn/wallets/add-network/cover.webp b/public/assets/images/learn/wallets/add-network/cover.webp new file mode 100644 index 000000000..cddee8988 Binary files /dev/null and b/public/assets/images/learn/wallets/add-network/cover.webp differ diff --git a/public/assets/images/learn/wallets/add-network/metamask-add-network-1.png b/public/assets/images/learn/wallets/add-network/metamask-add-network-1.png deleted file mode 100644 index ce347698e..000000000 Binary files a/public/assets/images/learn/wallets/add-network/metamask-add-network-1.png and /dev/null differ diff --git a/public/assets/images/learn/wallets/add-network/metamask-add-network-1.webp b/public/assets/images/learn/wallets/add-network/metamask-add-network-1.webp new file mode 100644 index 000000000..0bf23f45f Binary files /dev/null and b/public/assets/images/learn/wallets/add-network/metamask-add-network-1.webp differ diff --git a/public/assets/images/learn/wallets/add-network/metamask-add-network-2.png b/public/assets/images/learn/wallets/add-network/metamask-add-network-2.png deleted file mode 100644 index 5674cd944..000000000 Binary files a/public/assets/images/learn/wallets/add-network/metamask-add-network-2.png and /dev/null differ diff --git a/public/assets/images/learn/wallets/add-network/metamask-add-network-2.webp b/public/assets/images/learn/wallets/add-network/metamask-add-network-2.webp new file mode 100644 index 000000000..8a3185232 Binary files /dev/null and b/public/assets/images/learn/wallets/add-network/metamask-add-network-2.webp differ diff --git a/public/assets/images/learn/wallets/cover.jpg b/public/assets/images/learn/wallets/cover.jpg deleted file mode 100644 index 9910ae29a..000000000 Binary files a/public/assets/images/learn/wallets/cover.jpg and /dev/null differ diff --git a/public/assets/images/learn/wallets/cover.webp b/public/assets/images/learn/wallets/cover.webp new file mode 100644 index 000000000..c92162474 Binary files /dev/null and b/public/assets/images/learn/wallets/cover.webp differ diff --git a/public/assets/images/thumbnail/demo-thumbnail.webp b/public/assets/images/thumbnail/demo-thumbnail.webp new file mode 100644 index 000000000..6a17c5101 Binary files /dev/null and b/public/assets/images/thumbnail/demo-thumbnail.webp differ