Skip to content

Commit

Permalink
Analytics: track tx creations and safe views (#2787)
Browse files Browse the repository at this point in the history
* Feat: track tx creations and safe views

* Rm PROPOSE_TX, rename TOGGLE_EXECUTE_TX, fix SIMULATE_TX

* Fix tests

* Track only typed messages as tx_created

* Safe viewed = meta event

* Do not call onSubmit when adding to batch

* Track safe apps tx creation

* Analytics: track app version in all events (#2831)

* Fix: remove deprecated mobile pairing (#2794)

* Fix: remove deprecated mobile pairing

* Fix accounts[0] and Delete Account

* Update onboard package

* Update env vars

* Fix: add help link in footer (#2822)

* Fix: add help link in footer

* External link

* Fix: double scroll bar in safe apps (#2829)

* Feat: track app version in all events

* Tests

* Fix safeapps and walletconnect events

* Rm spending_limit_transfer
  • Loading branch information
katspaugh authored Nov 22, 2023
1 parent 9a7978c commit 8a216e0
Show file tree
Hide file tree
Showing 31 changed files with 325 additions and 87 deletions.
8 changes: 2 additions & 6 deletions src/components/dashboard/FeaturedApps/FeaturedApps.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,7 @@ import { SafeAppsTag } from '@/config/constants'
import { useRemoteSafeApps } from '@/hooks/safe-apps/useRemoteSafeApps'
import SafeAppIconCard from '@/components/safe-apps/SafeAppIconCard'
import { WalletConnectContext } from '@/services/walletconnect/WalletConnectContext'

const isWalletConnectSafeApp = (app: SafeAppData): boolean => {
const WALLET_CONNECT = /wallet-connect/
return WALLET_CONNECT.test(app.url)
}
import { isWalletConnectSafeApp } from '@/services/walletconnect/utils'

const FeaturedAppCard = ({ app }: { app: SafeAppData }) => (
<Card>
Expand Down Expand Up @@ -62,7 +58,7 @@ export const FeaturedApps = ({ stackedLayout }: { stackedLayout: boolean }): Rea
>
{featuredApps?.map((app) => (
<Grid item xs md key={app.id}>
{isWalletConnectSafeApp(app) ? (
{isWalletConnectSafeApp(app.url) ? (
<a onClick={onWcWidgetClick} style={{ cursor: 'pointer' }}>
<FeaturedAppCard app={app} />
</a>
Expand Down
2 changes: 2 additions & 0 deletions src/components/tx-flow/flows/AddOwner/ReviewOwner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import PlusIcon from '@/public/images/common/plus.svg'
import MinusIcon from '@/public/images/common/minus.svg'
import EthHashInfo from '@/components/common/EthHashInfo'
import commonCss from '@/components/tx-flow/common/styles.module.css'
import { TX_EVENTS, TX_TYPES } from '@/services/analytics/events/transactions'

export const ReviewOwner = ({ params }: { params: AddOwnerFlowProps | ReplaceOwnerFlowProps }) => {
const dispatch = useAppDispatch()
Expand Down Expand Up @@ -49,6 +50,7 @@ export const ReviewOwner = ({ params }: { params: AddOwnerFlowProps | ReplaceOwn

trackEvent({ ...SETTINGS_EVENTS.SETUP.THRESHOLD, label: safe.threshold })
trackEvent({ ...SETTINGS_EVENTS.SETUP.OWNERS, label: safe.owners.length })
trackEvent({ ...TX_EVENTS.CREATE, label: params.removedOwner ? TX_TYPES.owner_swap : TX_TYPES.owner_add })
}

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import SignOrExecuteForm from '@/components/tx/SignOrExecuteForm'
import { SafeTxContext } from '@/components/tx-flow/SafeTxProvider'
import { ChangeThresholdFlowFieldNames } from '@/components/tx-flow/flows/ChangeThreshold'
import type { ChangeThresholdFlowProps } from '@/components/tx-flow/flows/ChangeThreshold'
import { TX_EVENTS, TX_TYPES } from '@/services/analytics/events/transactions'

import commonCss from '@/components/tx-flow/common/styles.module.css'

Expand All @@ -24,6 +25,7 @@ const ReviewChangeThreshold = ({ params }: { params: ChangeThresholdFlowProps })
const onChangeThreshold = () => {
trackEvent({ ...SETTINGS_EVENTS.SETUP.OWNERS, label: safe.owners.length })
trackEvent({ ...SETTINGS_EVENTS.SETUP.THRESHOLD, label: newThreshold })
trackEvent({ ...TX_EVENTS.CREATE, label: TX_TYPES.owner_threshold_change })
}

return (
Expand Down
11 changes: 9 additions & 2 deletions src/components/tx-flow/flows/ConfirmBatch/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { type ReactElement, useContext, useEffect } from 'react'
import { type ReactElement, useContext, useEffect, useCallback } from 'react'
import { type TransactionDetails } from '@safe-global/safe-gateway-typescript-sdk'
import SignOrExecuteForm from '@/components/tx/SignOrExecuteForm'
import { createMultiSendCallOnlyTx } from '@/services/tx/tx-sender'
Expand All @@ -9,6 +9,8 @@ import TxLayout from '../../common/TxLayout'
import BatchIcon from '@/public/images/common/batch.svg'
import { useDraftBatch } from '@/hooks/useDraftBatch'
import BatchTxList from '@/components/batch/BatchSidebar/BatchTxList'
import { TX_EVENTS, TX_TYPES } from '@/services/analytics/events/transactions'
import { trackEvent } from '@/services/analytics'

type ConfirmBatchProps = {
onSubmit: () => void
Expand All @@ -32,8 +34,13 @@ const ConfirmBatch = ({ onSubmit }: ConfirmBatchProps): ReactElement => {
createMultiSendCallOnlyTx(calls).then(setSafeTx).catch(setSafeTxError)
}, [batchTxs, setSafeTx, setSafeTxError])

const onTxSubmit = useCallback(() => {
trackEvent({ ...TX_EVENTS.CREATE, label: TX_TYPES.batch })
onSubmit()
}, [onSubmit])

return (
<SignOrExecuteForm onSubmit={onSubmit} isBatch>
<SignOrExecuteForm onSubmit={onTxSubmit} isBatch>
<BatchTxList txItems={batchTxs} />
</SignOrExecuteForm>
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import type { SpendingLimitState } from '@/store/spendingLimitsSlice'
import type { NewSpendingLimitFlowProps } from '.'
import EthHashInfo from '@/components/common/EthHashInfo'
import { SafeTxContext } from '../../SafeTxProvider'
import { TX_EVENTS, TX_TYPES } from '@/services/analytics/events/transactions'

export const ReviewSpendingLimit = ({ params }: { params: NewSpendingLimitFlowProps }) => {
const [existingSpendingLimit, setExistingSpendingLimit] = useState<SpendingLimitState>()
Expand Down Expand Up @@ -53,6 +54,8 @@ export const ReviewSpendingLimit = ({ params }: { params: NewSpendingLimitFlowPr
...SETTINGS_EVENTS.SPENDING_LIMIT.RESET_PERIOD,
label: resetTime,
})

trackEvent({ ...TX_EVENTS.CREATE, label: TX_TYPES.spending_limit_add })
}

const existingAmount = existingSpendingLimit
Expand Down
11 changes: 9 additions & 2 deletions src/components/tx-flow/flows/NftTransfer/ReviewNftBatch.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { type ReactElement, useEffect, useContext } from 'react'
import { type ReactElement, useEffect, useContext, useCallback } from 'react'
import { Grid, Typography } from '@mui/material'
import SendToBlock from '@/components/tx-flow/flows/TokenTransfer/SendToBlock'
import { createNftTransferParams } from '@/services/tx/tokenTransferParams'
Expand All @@ -8,6 +8,8 @@ import { createMultiSendCallOnlyTx, createTx } from '@/services/tx/tx-sender'
import SignOrExecuteForm from '@/components/tx/SignOrExecuteForm'
import { SafeTxContext } from '../../SafeTxProvider'
import { NftItems } from '@/components/tx-flow/flows/NftTransfer/SendNftBatch'
import { TX_EVENTS, TX_TYPES } from '@/services/analytics/events/transactions'
import { trackEvent } from '@/services/analytics'

type ReviewNftBatchProps = {
params: NftTransferParams
Expand Down Expand Up @@ -38,8 +40,13 @@ const ReviewNftBatch = ({ params, onSubmit, txNonce }: ReviewNftBatchProps): Rea
promise.then(setSafeTx).catch(setSafeTxError)
}, [safeAddress, params, setSafeTx, setSafeTxError])

const onTxSubmit = useCallback(() => {
trackEvent({ ...TX_EVENTS.CREATE, label: TX_TYPES.transfer_nft })
onSubmit()
}, [onSubmit])

return (
<SignOrExecuteForm onSubmit={onSubmit}>
<SignOrExecuteForm onSubmit={onTxSubmit}>
<Grid container gap={1} mb={2}>
<Grid item md>
<Typography variant="body2" color="text.secondary">
Expand Down
8 changes: 7 additions & 1 deletion src/components/tx-flow/flows/RejectTx/RejectTx.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,17 @@ import SignOrExecuteForm from '@/components/tx/SignOrExecuteForm'
import { createRejectTx } from '@/services/tx/tx-sender'
import { useContext, useEffect } from 'react'
import { SafeTxContext } from '../../SafeTxProvider'
import { trackEvent } from '@/services/analytics'
import { TX_EVENTS, TX_TYPES } from '@/services/analytics/events/transactions'

type RejectTxProps = {
txNonce: number
}

const onSubmit = () => {
trackEvent({ ...TX_EVENTS.CREATE, label: TX_TYPES.rejection })
}

const RejectTx = ({ txNonce }: RejectTxProps): ReactElement => {
const { setSafeTx, setSafeTxError, setNonce } = useContext(SafeTxContext)

Expand All @@ -19,7 +25,7 @@ const RejectTx = ({ txNonce }: RejectTxProps): ReactElement => {
}, [txNonce, setNonce, setSafeTx, setSafeTxError])

return (
<SignOrExecuteForm onSubmit={() => {}} isBatchable={false}>
<SignOrExecuteForm onSubmit={onSubmit} isBatchable={false}>
<Typography mb={2}>
To reject the transaction, a separate rejection transaction will be created to replace the original one.
</Typography>
Expand Down
10 changes: 6 additions & 4 deletions src/components/tx-flow/flows/RemoveGuard/ReviewRemoveGuard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ import { trackEvent, SETTINGS_EVENTS } from '@/services/analytics'
import { createRemoveGuardTx } from '@/services/tx/tx-sender'
import { type RemoveGuardFlowProps } from '.'
import { SafeTxContext } from '@/components/tx-flow/SafeTxProvider'
import { TX_EVENTS, TX_TYPES } from '@/services/analytics/events/transactions'

const onFormSubmit = () => {
trackEvent(SETTINGS_EVENTS.MODULES.REMOVE_GUARD)
trackEvent({ ...TX_EVENTS.CREATE, label: TX_TYPES.guard_remove })
}

export const ReviewRemoveGuard = ({ params }: { params: RemoveGuardFlowProps }) => {
const { setSafeTx, safeTxError, setSafeTxError } = useContext(SafeTxContext)
Expand All @@ -21,10 +27,6 @@ export const ReviewRemoveGuard = ({ params }: { params: RemoveGuardFlowProps })
}
}, [safeTxError])

const onFormSubmit = () => {
trackEvent(SETTINGS_EVENTS.MODULES.REMOVE_GUARD)
}

return (
<SignOrExecuteForm onSubmit={onFormSubmit}>
<Typography sx={({ palette }) => ({ color: palette.primary.light })}>Transaction guard</Typography>
Expand Down
10 changes: 6 additions & 4 deletions src/components/tx-flow/flows/RemoveModule/ReviewRemoveModule.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ import { createRemoveModuleTx } from '@/services/tx/tx-sender'
import { SafeTxContext } from '@/components/tx-flow/SafeTxProvider'
import { type RemoveModuleFlowProps } from '.'
import EthHashInfo from '@/components/common/EthHashInfo'
import { TX_EVENTS, TX_TYPES } from '@/services/analytics/events/transactions'

const onFormSubmit = () => {
trackEvent(SETTINGS_EVENTS.MODULES.REMOVE_MODULE)
trackEvent({ ...TX_EVENTS.CREATE, label: TX_TYPES.module_remove })
}

export const ReviewRemoveModule = ({ params }: { params: RemoveModuleFlowProps }) => {
const { setSafeTx, safeTxError, setSafeTxError } = useContext(SafeTxContext)
Expand All @@ -21,10 +27,6 @@ export const ReviewRemoveModule = ({ params }: { params: RemoveModuleFlowProps }
}
}, [safeTxError])

const onFormSubmit = () => {
trackEvent(SETTINGS_EVENTS.MODULES.REMOVE_MODULE)
}

return (
<SignOrExecuteForm onSubmit={onFormSubmit}>
<Grid container gap={1} alignItems="center">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import MinusIcon from '@/public/images/common/minus.svg'
import { SafeTxContext } from '../../SafeTxProvider'
import type { RemoveOwnerFlowProps } from '.'
import EthHashInfo from '@/components/common/EthHashInfo'
import { TX_EVENTS, TX_TYPES } from '@/services/analytics/events/transactions'

import commonCss from '@/components/tx-flow/common/styles.module.css'

Expand All @@ -29,6 +30,7 @@ export const ReviewRemoveOwner = ({ params }: { params: RemoveOwnerFlowProps }):
const onFormSubmit = () => {
trackEvent({ ...SETTINGS_EVENTS.SETUP.THRESHOLD, label: safe.threshold })
trackEvent({ ...SETTINGS_EVENTS.SETUP.OWNERS, label: safe.owners.length })
trackEvent({ ...TX_EVENTS.CREATE, label: TX_TYPES.owner_remove })
}

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ import SendAmountBlock from '@/components/tx-flow/flows/TokenTransfer/SendAmount
import { safeFormatUnits } from '@/utils/formatters'
import SpendingLimitLabel from '@/components/common/SpendingLimitLabel'
import { createTx } from '@/services/tx/tx-sender'
import { TX_EVENTS, TX_TYPES } from '@/services/analytics/events/transactions'

const onFormSubmit = () => {
trackEvent(SETTINGS_EVENTS.SPENDING_LIMIT.LIMIT_REMOVED)
trackEvent({ ...TX_EVENTS.CREATE, label: TX_TYPES.spending_limit_remove })
}

export const RemoveSpendingLimit = ({ params }: { params: SpendingLimitState }) => {
const { setSafeTx, setSafeTxError } = useContext(SafeTxContext)
Expand Down Expand Up @@ -42,10 +48,6 @@ export const RemoveSpendingLimit = ({ params }: { params: SpendingLimitState })
createTx(txParams).then(setSafeTx).catch(setSafeTxError)
}, [chainId, params.beneficiary, params.token, setSafeTx, setSafeTxError])

const onFormSubmit = () => {
trackEvent(SETTINGS_EVENTS.SPENDING_LIMIT.LIMIT_REMOVED)
}

return (
<SignOrExecuteForm onSubmit={onFormSubmit}>
{token && (
Expand Down
18 changes: 17 additions & 1 deletion src/components/tx-flow/flows/SafeAppsTx/ReviewSafeAppsTx.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,18 @@ import ApprovalEditor from '@/components/tx/ApprovalEditor'
import { getInteractionTitle, isTxValid } from '@/components/safe-apps/utils'
import ErrorMessage from '@/components/tx/ErrorMessage'
import { asError } from '@/services/exceptions/utils'
import { trackEvent } from '@/services/analytics'
import { TX_EVENTS, TX_TYPES } from '@/services/analytics/events/transactions'
import { isWalletConnectSafeApp } from '@/services/walletconnect/utils'

type ReviewSafeAppsTxProps = {
safeAppsTx: SafeAppsTxParams
onSubmit?: (txId: string, safeTxHash: string) => void
}

const ReviewSafeAppsTx = ({
safeAppsTx: { txs, requestId, params, appId, app },
onSubmit,
}: ReviewSafeAppsTxProps): ReactElement => {
const { safe } = useSafeInfo()
const onboard = useOnboard()
Expand Down Expand Up @@ -54,11 +59,22 @@ const ReviewSafeAppsTx = ({
if (!safeTx || !onboard) return
trackSafeAppTxCount(Number(appId))

let safeTxHash = ''
try {
await dispatchSafeAppsTx(safeTx, requestId, onboard, safe.chainId, txId)
safeTxHash = await dispatchSafeAppsTx(safeTx, requestId, onboard, safe.chainId, txId)
} catch (error) {
setSafeTxError(asError(error))
}

// Track tx creation
if (safeTx.signatures.size === 0) {
trackEvent({
...TX_EVENTS.CREATE,
label: isWalletConnectSafeApp(app?.url || '') ? TX_TYPES.walletconnect : TX_TYPES.safeapps,
})
}

onSubmit?.(txId, safeTxHash)
}

const origin = useMemo(() => getTxOrigin(app), [app])
Expand Down
10 changes: 8 additions & 2 deletions src/components/tx-flow/flows/SafeAppsTx/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,20 @@ export type SafeAppsTxParams = {
params?: SendTransactionRequestParams
}

const SafeAppsTxFlow = ({ data }: { data: SafeAppsTxParams }) => {
const SafeAppsTxFlow = ({
data,
onSubmit,
}: {
data: SafeAppsTxParams
onSubmit?: (txId: string, safeTxHash: string) => void
}) => {
return (
<TxLayout
title="Confirm transaction"
subtitle={<AppTitle name={data.app?.name} logoUri={data.app?.iconUrl} />}
step={0}
>
<ReviewSafeAppsTx safeAppsTx={data} />
<ReviewSafeAppsTx safeAppsTx={data} onSubmit={onSubmit} />
</TxLayout>
)
}
Expand Down
19 changes: 11 additions & 8 deletions src/components/tx-flow/flows/SignMessage/SignMessage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ import InfoBox from '@/components/safe-messages/InfoBox'
import { DecodedMsg } from '@/components/safe-messages/DecodedMsg'
import TxCard from '@/components/tx-flow/common/TxCard'
import { dispatchPreparedSignature } from '@/services/safe-messages/safeMsgNotifications'
import { trackEvent } from '@/services/analytics'
import { TX_EVENTS, TX_TYPES } from '@/services/analytics/events/transactions'

const createSkeletonMessage = (confirmationsRequired: number): SafeMessage => {
return {
Expand Down Expand Up @@ -169,19 +171,14 @@ const SignMessage = ({ message, safeAppId, requestId }: ProposeProps | ConfirmPr
const { safe } = useSafeInfo()
const isOwner = useIsSafeOwner()
const wallet = useWallet()
useHighlightHiddenTab()

const { decodedMessage, safeMessageMessage, safeMessageHash } = useDecodedSafeMessage(message, safe)
const [safeMessage, setSafeMessage] = useSafeMessage(safeMessageHash)

useHighlightHiddenTab()

const decodedMessageAsString =
typeof decodedMessage === 'string' ? decodedMessage : JSON.stringify(decodedMessage, null, 2)

const isPlainTextMessage = typeof decodedMessage === 'string'
const decodedMessageAsString = isPlainTextMessage ? decodedMessage : JSON.stringify(decodedMessage, null, 2)
const hasSigned = !!safeMessage?.confirmations.some(({ owner }) => owner.value === wallet?.address)

const isFullySigned = !!safeMessage?.preparedSignature

const isDisabled = !isOwner || hasSigned

const { onSign, submitError } = useSyncSafeMessageSigner(
Expand All @@ -195,9 +192,15 @@ const SignMessage = ({ message, safeAppId, requestId }: ProposeProps | ConfirmPr

const handleSign = async () => {
const updatedMessage = await onSign()

if (updatedMessage) {
setSafeMessage(updatedMessage)
}

// Track first signature as creation
if (updatedMessage?.confirmations.length === 1) {
trackEvent({ ...TX_EVENTS.CREATE, label: TX_TYPES.typed_message })
}
}

const onContinue = async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ import useHighlightHiddenTab from '@/hooks/useHighlightHiddenTab'
import { type SafeAppData } from '@safe-global/safe-gateway-typescript-sdk'
import { SafeTxContext } from '@/components/tx-flow/SafeTxProvider'
import { asError } from '@/services/exceptions/utils'
import { trackEvent } from '@/services/analytics'
import { TX_EVENTS, TX_TYPES } from '@/services/analytics/events/transactions'

export type SignMessageOnChainProps = {
app?: SafeAppData
Expand Down Expand Up @@ -98,6 +100,12 @@ const ReviewSignMessageOnChain = ({ message, method, requestId }: SignMessageOnC

const handleSubmit = async () => {
if (!safeTx || !onboard) return

// Track the creation of a typed message
if (isTypedMessage && safeTx.signatures.size === 1) {
trackEvent({ ...TX_EVENTS.CREATE, label: TX_TYPES.typed_message })
}

try {
await dispatchSafeAppsTx(safeTx, requestId, onboard, safe.chainId)
} catch (error) {
Expand Down
Loading

0 comments on commit 8a216e0

Please sign in to comment.