From 459a851ba8dab8ba280844391555884f68f25918 Mon Sep 17 00:00:00 2001 From: Usame Algan Date: Thu, 15 Feb 2024 20:32:23 +0100 Subject: [PATCH] fix: [Counterfactual] Add analytics events --- .../dashboard/CreationDialog/index.tsx | 20 ++++--- src/components/dashboard/FirstSteps/index.tsx | 18 ++++-- .../create/steps/ReviewStep/index.tsx | 5 +- .../counterfactual/ActivateAccountFlow.tsx | 4 ++ src/features/counterfactual/CheckBalance.tsx | 6 +- .../counterfactual/CounterfactualForm.tsx | 3 + src/features/counterfactual/FirstTxFlow.tsx | 8 +++ .../counterfactual/PayNowPayLater.tsx | 5 ++ .../hooks/usePendingSafeStatuses.ts | 3 + src/features/counterfactual/utils.ts | 29 +--------- .../analytics/events/counterfactual.ts | 56 +++++++++++++++++++ 11 files changed, 112 insertions(+), 45 deletions(-) create mode 100644 src/services/analytics/events/counterfactual.ts diff --git a/src/components/dashboard/CreationDialog/index.tsx b/src/components/dashboard/CreationDialog/index.tsx index a4d7440a45..8b507f2ddb 100644 --- a/src/components/dashboard/CreationDialog/index.tsx +++ b/src/components/dashboard/CreationDialog/index.tsx @@ -1,9 +1,11 @@ -import { selectUndeployedSafe } from '@/features/counterfactual/store/undeployedSafesSlice' +import { selectUndeployedSafe, type UndeployedSafe } from '@/features/counterfactual/store/undeployedSafesSlice' import useChainId from '@/hooks/useChainId' import useSafeInfo from '@/hooks/useSafeInfo' +import { trackEvent } from '@/services/analytics' +import { COUNTERFACTUAL_EVENTS } from '@/services/analytics/events/counterfactual' import { useAppSelector } from '@/store' import type { PredictedSafeProps } from '@safe-global/protocol-kit' -import React, { type ElementType } from 'react' +import React, { type ElementType, type MouseEvent } from 'react' import { Alert, Box, Button, Dialog, DialogContent, Grid, Link, SvgIcon, Typography } from '@mui/material' import { useRouter } from 'next/router' @@ -68,6 +70,12 @@ const CreationDialog = () => { setOpen(false) } + const onBackup = (e: MouseEvent, undeployedSafe: UndeployedSafe) => { + e.preventDefault() + trackEvent(COUNTERFACTUAL_EVENTS.BACKUP_COUNTERFACTUAL_SAFE) + backupSafe(chainId, safeAddress, undeployedSafe.props) + } + return ( @@ -106,13 +114,7 @@ const CreationDialog = () => { {undeployedSafe && ( We recommend{' '} - { - e.preventDefault() - backupSafe(chainId, safeAddress, undeployedSafe.props) - }} - > + onBackup(e, undeployedSafe)}> backing up your Safe Account {' '} in case you lose access to this device. diff --git a/src/components/dashboard/FirstSteps/index.tsx b/src/components/dashboard/FirstSteps/index.tsx index 1d38292e18..2cb8510ebb 100644 --- a/src/components/dashboard/FirstSteps/index.tsx +++ b/src/components/dashboard/FirstSteps/index.tsx @@ -2,10 +2,12 @@ import { BuyCryptoOptions } from '@/components/common/BuyCryptoButton' import EthHashInfo from '@/components/common/EthHashInfo' import ModalDialog from '@/components/common/ModalDialog' import QRCode from '@/components/common/QRCode' +import Track from '@/components/common/Track' import FirstTxFlow from '@/features/counterfactual/FirstTxFlow' import useBalances from '@/hooks/useBalances' import { useCurrentChain } from '@/hooks/useChains' import useSafeInfo from '@/hooks/useSafeInfo' +import { COUNTERFACTUAL_EVENTS } from '@/services/analytics/events/counterfactual' import { useAppSelector } from '@/store' import { selectOutgoingTransactions } from '@/store/txHistorySlice' import classnames from 'classnames' @@ -74,9 +76,11 @@ const AddFundsWidget = ({ completed }: { completed: boolean }) => { {!completed && ( <> - + + + { <> {!completed && ( - + + + )} setOpen(false)} /> diff --git a/src/components/new-safe/create/steps/ReviewStep/index.tsx b/src/components/new-safe/create/steps/ReviewStep/index.tsx index 1b0c97b4d7..37dcf860d2 100644 --- a/src/components/new-safe/create/steps/ReviewStep/index.tsx +++ b/src/components/new-safe/create/steps/ReviewStep/index.tsx @@ -25,6 +25,8 @@ import { MAX_HOUR_RELAYS, useLeastRemainingRelays } from '@/hooks/useRemainingRe import useWalletCanPay from '@/hooks/useWalletCanPay' import useWallet from '@/hooks/wallets/useWallet' import { useWeb3 } from '@/hooks/wallets/web3' +import { trackEvent } from '@/services/analytics' +import { COUNTERFACTUAL_EVENTS } from '@/services/analytics/events/counterfactual' import { getReadOnlyFallbackHandlerContract } from '@/services/contracts/safeContracts' import { isSocialLoginWallet } from '@/services/mpc/SocialLoginModule' import { useAppDispatch } from '@/store' @@ -211,6 +213,7 @@ const ReviewStep = ({ data, onSubmit, onBack, setStep }: StepRenderProps - + You will have to confirm a transaction and pay an estimated fee of{' '} with your connected wallet diff --git a/src/features/counterfactual/ActivateAccountFlow.tsx b/src/features/counterfactual/ActivateAccountFlow.tsx index cc833602d2..7b32c4a9cd 100644 --- a/src/features/counterfactual/ActivateAccountFlow.tsx +++ b/src/features/counterfactual/ActivateAccountFlow.tsx @@ -21,6 +21,8 @@ import useSafeInfo from '@/hooks/useSafeInfo' import useWalletCanPay from '@/hooks/useWalletCanPay' import useWallet from '@/hooks/wallets/useWallet' import { useWeb3 } from '@/hooks/wallets/web3' +import { trackEvent } from '@/services/analytics' +import { COUNTERFACTUAL_EVENTS } from '@/services/analytics/events/counterfactual' import { asError } from '@/services/exceptions/utils' import { isSocialLoginWallet } from '@/services/mpc/SocialLoginModule' import { useAppSelector } from '@/store' @@ -86,6 +88,7 @@ const ActivateAccountFlow = () => { } const onSubmit = (txHash?: string) => { + trackEvent({ ...COUNTERFACTUAL_EVENTS.SUBMIT_ACCOUNT_ACTIVATION, label: 'without_tx' }) if (txHash) { safeCreationDispatch(SafeCreationEvent.PROCESSING, { groupKey: CF_TX_GROUP_KEY, txHash }) } @@ -209,6 +212,7 @@ export const ActivateAccountButton = () => { const isProcessing = undeployedSafe?.status.status !== PendingSafeStatus.AWAITING_EXECUTION const activateAccount = () => { + trackEvent({ ...COUNTERFACTUAL_EVENTS.CHOOSE_FIRST_TRANSACTION, label: 'activate_now' }) setTxFlow() } diff --git a/src/features/counterfactual/CheckBalance.tsx b/src/features/counterfactual/CheckBalance.tsx index 3d2a31ef9c..13a9901166 100644 --- a/src/features/counterfactual/CheckBalance.tsx +++ b/src/features/counterfactual/CheckBalance.tsx @@ -1,7 +1,9 @@ import ExternalLink from '@/components/common/ExternalLink' +import Track from '@/components/common/Track' import { ActivateAccountButton } from '@/features/counterfactual/ActivateAccountFlow' import { useCurrentChain } from '@/hooks/useChains' import useSafeInfo from '@/hooks/useSafeInfo' +import { COUNTERFACTUAL_EVENTS } from '@/services/analytics/events/counterfactual' import { getBlockExplorerLink } from '@/utils/chains' import { Alert, Typography } from '@mui/material' @@ -24,7 +26,9 @@ const CheckBalance = () => { {blockExplorerLink && ( <> You can always view all of your assets on the{' '} - Block Explorer + + Block Explorer + )} diff --git a/src/features/counterfactual/CounterfactualForm.tsx b/src/features/counterfactual/CounterfactualForm.tsx index 3c0447d9a8..c45a888896 100644 --- a/src/features/counterfactual/CounterfactualForm.tsx +++ b/src/features/counterfactual/CounterfactualForm.tsx @@ -7,6 +7,8 @@ import useSafeInfo from '@/hooks/useSafeInfo' import useWalletCanPay from '@/hooks/useWalletCanPay' import useOnboard from '@/hooks/wallets/useOnboard' import useWallet from '@/hooks/wallets/useWallet' +import { trackEvent } from '@/services/analytics' +import { COUNTERFACTUAL_EVENTS } from '@/services/analytics/events/counterfactual' import { useAppDispatch } from '@/store' import madProps from '@/utils/mad-props' import React, { type ReactElement, type SyntheticEvent, useContext, useState } from 'react' @@ -80,6 +82,7 @@ export const CounterfactualForm = ({ try { await deploySafeAndExecuteTx(txOptions, chainId, wallet, safeTx, onboard) + trackEvent({ ...COUNTERFACTUAL_EVENTS.SUBMIT_ACCOUNT_ACTIVATION, label: 'with_tx' }) } catch (_err) { const err = asError(_err) trackError(Errors._804, err) diff --git a/src/features/counterfactual/FirstTxFlow.tsx b/src/features/counterfactual/FirstTxFlow.tsx index 278a30c643..34d99e6e2e 100644 --- a/src/features/counterfactual/FirstTxFlow.tsx +++ b/src/features/counterfactual/FirstTxFlow.tsx @@ -1,5 +1,7 @@ import { AppRoutes } from '@/config/routes' import ActivateAccountFlow from '@/features/counterfactual/ActivateAccountFlow' +import { trackEvent } from '@/services/analytics' +import { COUNTERFACTUAL_EVENTS } from '@/services/analytics/events/counterfactual' import { useRouter } from 'next/router' import { type ElementType, useContext } from 'react' import { Box, ButtonBase, Grid, SvgIcon, Typography } from '@mui/material' @@ -57,29 +59,35 @@ const FirstTxFlow = ({ open, onClose }: { open: boolean; onClose: () => void }) } const onSendToken = () => { + trackEvent({ ...COUNTERFACTUAL_EVENTS.CHOOSE_FIRST_TRANSACTION, label: 'send_token' }) setTxFlow() } const onActivateSafe = () => { + trackEvent({ ...COUNTERFACTUAL_EVENTS.CHOOSE_FIRST_TRANSACTION, label: 'activate_safe' }) setTxFlow() } const onAddSigner = () => { + trackEvent({ ...COUNTERFACTUAL_EVENTS.CHOOSE_FIRST_TRANSACTION, label: 'add_signer' }) setTxFlow() } const onRecovery = () => { + trackEvent({ ...COUNTERFACTUAL_EVENTS.CHOOSE_FIRST_TRANSACTION, label: 'setup_recovery' }) setTxFlow() } const onSwap = () => { // TODO: Pre-filter DeFi category apps + trackEvent({ ...COUNTERFACTUAL_EVENTS.CHOOSE_FIRST_TRANSACTION, label: 'swap' }) router.push({ pathname: AppRoutes.apps.index, query: router.query }) } const onCustomTransaction = () => { if (!txBuilder) return + trackEvent({ ...COUNTERFACTUAL_EVENTS.CHOOSE_FIRST_TRANSACTION, label: 'tx_builder' }) router.push(txBuilder.link) } diff --git a/src/features/counterfactual/PayNowPayLater.tsx b/src/features/counterfactual/PayNowPayLater.tsx index 78e2cecece..faf77f2b3c 100644 --- a/src/features/counterfactual/PayNowPayLater.tsx +++ b/src/features/counterfactual/PayNowPayLater.tsx @@ -1,3 +1,5 @@ +import { trackEvent } from '@/services/analytics' +import { COUNTERFACTUAL_EVENTS } from '@/services/analytics/events/counterfactual' import type { ChangeEvent, Dispatch, SetStateAction } from 'react' import classnames from 'classnames' import { useCurrentChain } from '@/hooks/useChains' @@ -34,6 +36,9 @@ const PayNowPayLater = ({ const chain = useCurrentChain() const onChoosePayMethod = (_: ChangeEvent, newPayMethod: string) => { + trackEvent( + newPayMethod === PayMethod.PayNow ? COUNTERFACTUAL_EVENTS.CHOOSE_PAY_NOW : COUNTERFACTUAL_EVENTS.CHOOSE_PAY_LATER, + ) setPayMethod(newPayMethod as PayMethod) } diff --git a/src/features/counterfactual/hooks/usePendingSafeStatuses.ts b/src/features/counterfactual/hooks/usePendingSafeStatuses.ts index e724aa7cf6..eeac1db7b8 100644 --- a/src/features/counterfactual/hooks/usePendingSafeStatuses.ts +++ b/src/features/counterfactual/hooks/usePendingSafeStatuses.ts @@ -14,6 +14,8 @@ import { checkSafeActionViaRelay, checkSafeActivation } from '@/features/counter import useChainId from '@/hooks/useChainId' import useSafeInfo from '@/hooks/useSafeInfo' import { isSmartContract, useWeb3ReadOnly } from '@/hooks/wallets/web3' +import { trackEvent } from '@/services/analytics' +import { COUNTERFACTUAL_EVENTS } from '@/services/analytics/events/counterfactual' import { useAppDispatch, useAppSelector } from '@/store' import { useEffect, useRef } from 'react' @@ -99,6 +101,7 @@ const usePendingSafeStatus = (): void => { const unsubFns = Object.entries(safeCreationPendingStatuses).map(([event, status]) => safeCreationSubscribe(event as SafeCreationEvent, async (detail) => { if (event === SafeCreationEvent.SUCCESS) { + trackEvent(COUNTERFACTUAL_EVENTS.DEPLOYED_COUNTERFACTUAL_SAFE) pollSafeInfo(safe.chainId, safeAddress).finally(() => { safeCreationDispatch(SafeCreationEvent.INDEXED, { groupKey: detail.groupKey, safeAddress }) }) diff --git a/src/features/counterfactual/utils.ts b/src/features/counterfactual/utils.ts index e23e986ed7..1d6f861b30 100644 --- a/src/features/counterfactual/utils.ts +++ b/src/features/counterfactual/utils.ts @@ -16,7 +16,7 @@ import { upsertAddressBookEntry } from '@/store/addressBookSlice' import { showNotification } from '@/store/notificationsSlice' import { defaultSafeInfo } from '@/store/safeInfoSlice' import { getBlockExplorerLink } from '@/utils/chains' -import { didReprice, didRevert, type EthersError } from '@/utils/ethers-utils' +import { didRevert, type EthersError } from '@/utils/ethers-utils' import { assertOnboard, assertTx, assertWallet } from '@/utils/helpers' import type { DeploySafeProps, PredictedSafeProps } from '@safe-global/protocol-kit' import { ZERO_ADDRESS } from '@safe-global/protocol-kit/dist/src/utils/constants' @@ -81,33 +81,6 @@ export const dispatchTxExecutionAndDeploySafe = async ( safeCreationDispatch(SafeCreationEvent.PROCESSING, { ...eventParams, txHash: result!.hash }) - result - ?.wait() - .then((receipt) => { - if (receipt === null) { - safeCreationDispatch(SafeCreationEvent.FAILED, { - ...eventParams, - error: new Error('No transaction receipt found'), - }) - } else if (didRevert(receipt)) { - safeCreationDispatch(SafeCreationEvent.REVERTED, { - ...eventParams, - error: new Error('Transaction reverted by EVM'), - }) - } else { - safeCreationDispatch(SafeCreationEvent.SUCCESS, { ...eventParams, safeAddress }) - } - }) - .catch((err) => { - const error = err as EthersError - - if (didReprice(error)) { - safeCreationDispatch(SafeCreationEvent.SUCCESS, { ...eventParams, safeAddress }) - } else { - safeCreationDispatch(SafeCreationEvent.FAILED, { ...eventParams, error: asError(error) }) - } - }) - return result!.hash } diff --git a/src/services/analytics/events/counterfactual.ts b/src/services/analytics/events/counterfactual.ts new file mode 100644 index 0000000000..2578fcca57 --- /dev/null +++ b/src/services/analytics/events/counterfactual.ts @@ -0,0 +1,56 @@ +import { EventType } from '@/services/analytics' + +const COUNTERFACTUAL_CATEGORY = 'counterfactual' + +export const COUNTERFACTUAL_EVENTS = { + CREATE_COUNTERFACTUAL_SAFE: { + action: 'Create counterfactual safe', + category: COUNTERFACTUAL_CATEGORY, + event: EventType.CLICK, + }, + CHOOSE_PAY_NOW: { + action: 'Choose pay now', + category: COUNTERFACTUAL_CATEGORY, + event: EventType.CLICK, + }, + CHOOSE_PAY_LATER: { + action: 'Choose pay later', + category: COUNTERFACTUAL_CATEGORY, + event: EventType.CLICK, + }, + BACKUP_COUNTERFACTUAL_SAFE: { + action: 'Backup counterfactual safe', + category: COUNTERFACTUAL_CATEGORY, + event: EventType.CLICK, + }, + CHOOSE_FIRST_TRANSACTION: { + action: 'Choose first transaction', + category: COUNTERFACTUAL_CATEGORY, + event: EventType.CLICK, + }, + PRESS_CREATE_TRANSACTION: { + action: 'Press create transaction', + category: COUNTERFACTUAL_CATEGORY, + event: EventType.CLICK, + }, + PRESS_ADD_FUNDS: { + action: 'Press add funds', + category: COUNTERFACTUAL_CATEGORY, + event: EventType.CLICK, + }, + SUBMIT_ACCOUNT_ACTIVATION: { + action: 'Submit account activation', + category: COUNTERFACTUAL_CATEGORY, + event: EventType.CLICK, + }, + DEPLOYED_COUNTERFACTUAL_SAFE: { + action: 'Deployed counterfactual safe', + category: COUNTERFACTUAL_CATEGORY, + event: EventType.META, + }, + CHECK_BALANCES: { + action: 'Check balances on block explorer', + category: COUNTERFACTUAL_CATEGORY, + event: EventType.CLICK, + }, +}