diff --git a/src/components/walletconnect/WcHeaderWidget/WcIcon.tsx b/src/components/walletconnect/WcHeaderWidget/WcIcon.tsx index e2cdb2ee8b..e83ee448c6 100644 --- a/src/components/walletconnect/WcHeaderWidget/WcIcon.tsx +++ b/src/components/walletconnect/WcHeaderWidget/WcIcon.tsx @@ -3,6 +3,8 @@ import { Badge, ButtonBase, SvgIcon } from '@mui/material' import WalletConnectIcon from '@/public/images/common/walletconnect.svg' import { useDarkMode } from '@/hooks/useDarkMode' import SafeAppIconCard from '@/components/safe-apps/SafeAppIconCard' +import { WALLETCONNECT_EVENTS } from '@/services/analytics/events/walletconnect' +import Track from '@/components/common/Track' type WcIconProps = { onClick: () => void @@ -16,25 +18,27 @@ const WcIcon = ({ sessionCount, sessionIcon, isError, onClick }: WcIconProps): R const showIcon = sessionCount === 1 && !!sessionIcon return ( - - - ) : ( - sessionCount - ) - } - color={isError ? 'error' : showIcon ? undefined : isDarkMode ? 'primary' : 'secondary'} - anchorOrigin={{ - vertical: 'bottom', - horizontal: 'right', - }} - > - - - + + + + ) : ( + sessionCount + ) + } + color={isError ? 'error' : showIcon ? undefined : isDarkMode ? 'primary' : 'secondary'} + anchorOrigin={{ + vertical: 'bottom', + horizontal: 'right', + }} + > + + + + ) } diff --git a/src/components/walletconnect/WcHints/index.tsx b/src/components/walletconnect/WcHints/index.tsx index c5539a2bbf..57b254ee61 100644 --- a/src/components/walletconnect/WcHints/index.tsx +++ b/src/components/walletconnect/WcHints/index.tsx @@ -12,13 +12,15 @@ import { ListItemAvatar, ListItemText, } from '@mui/material' -import { useState } from 'react' +import { useEffect, useState } from 'react' import type { ReactElement } from 'react' import { useCurrentChain } from '@/hooks/useChains' import Question from '@/public/images/common/question.svg' import css from './styles.module.css' +import { trackEvent } from '@/services/analytics' +import { WALLETCONNECT_EVENTS } from '@/services/analytics/events/walletconnect' const HintAccordion = ({ title, @@ -82,12 +84,18 @@ const WcHints = (): ReactElement => { setExpandedAccordion((prev) => { return prev === accordion ? null : accordion }) + + trackEvent(WALLETCONNECT_EVENTS.HINTS_EXPAND) } if (chain?.chainName) { InteractionSteps[1] = InteractionSteps[1].replace(/%%chain%%/, chain?.chainName) } + useEffect(() => { + trackEvent(WALLETCONNECT_EVENTS.HINTS_SHOW) + }, []) + return ( { const { walletConnect } = useContext(WalletConnectContext) @@ -59,9 +61,11 @@ const WcInput = ({ uri }: { uri: string }) => { InputProps={{ endAdornment: isClipboardSupported() ? undefined : ( - + + + ), }} diff --git a/src/components/walletconnect/WcProposalForm/index.tsx b/src/components/walletconnect/WcProposalForm/index.tsx index bc96929fe3..1259a82762 100644 --- a/src/components/walletconnect/WcProposalForm/index.tsx +++ b/src/components/walletconnect/WcProposalForm/index.tsx @@ -1,6 +1,6 @@ import { Button, Checkbox, Divider, FormControlLabel, Typography } from '@mui/material' -import { useMemo, useState } from 'react' -import type { ReactElement } from 'react' +import { useCallback, useEffect, useMemo, useState } from 'react' +import type { ReactElement, ChangeEvent } from 'react' import type { Web3WalletTypes } from '@walletconnect/web3wallet' import SafeAppIconCard from '@/components/safe-apps/SafeAppIconCard' @@ -10,6 +10,8 @@ import { CompatibilityWarning } from './CompatibilityWarning' import useChains from '@/hooks/useChains' import { getPeerName, getSupportedChainIds, isBlockedBridge, isWarnedBridge } from '@/services/walletconnect/utils' import useChainId from '@/hooks/useChainId' +import { trackEvent } from '@/services/analytics' +import { WALLETCONNECT_EVENTS } from '@/services/analytics/events/walletconnect' type ProposalFormProps = { proposal: Web3WalletTypes.SessionProposal @@ -23,6 +25,7 @@ const WcProposalForm = ({ proposal, onApprove, onReject }: ProposalFormProps): R const [understandsRisk, setUnderstandsRisk] = useState(false) const { proposer } = proposal.params const { isScam, origin } = proposal.verifyContext.verified + const url = proposer.metadata.url || origin const chainIds = useMemo(() => getSupportedChainIds(configs, proposal.params), [configs, proposal.params]) const isUnsupportedChain = !chainIds.includes(chainId) @@ -31,6 +34,29 @@ const WcProposalForm = ({ proposal, onApprove, onReject }: ProposalFormProps): R const isHighRisk = proposal.verifyContext.verified.validation === 'INVALID' || isWarnedBridge(origin, name) const disabled = isUnsupportedChain || isScam || isBlockedBridge(origin) || (isHighRisk && !understandsRisk) + const onCheckboxClick = useCallback( + (_: ChangeEvent, checked: boolean) => { + setUnderstandsRisk(checked) + + if (checked) { + trackEvent({ + ...WALLETCONNECT_EVENTS.ACCEPT_RISK, + label: url, + }) + } + }, + [url], + ) + + useEffect(() => { + if (isHighRisk || disabled) { + trackEvent({ + ...WALLETCONNECT_EVENTS.SHOW_RISK, + label: url, + }) + } + }, [isHighRisk, disabled, url]) + return (
@@ -60,7 +86,7 @@ const WcProposalForm = ({ proposal, onApprove, onReject }: ProposalFormProps): R {!isUnsupportedChain && isHighRisk && ( setUnderstandsRisk(checked)} />} + control={} label="I understand the risks associated with interacting with this dApp and would like to continue." /> )} diff --git a/src/components/walletconnect/WcSessionMananger/index.tsx b/src/components/walletconnect/WcSessionMananger/index.tsx index 624f40aee3..d8c93e2ec7 100644 --- a/src/components/walletconnect/WcSessionMananger/index.tsx +++ b/src/components/walletconnect/WcSessionMananger/index.tsx @@ -8,6 +8,8 @@ import WcConnectionForm from '../WcConnectionForm' import WcErrorMessage from '../WcErrorMessage' import WcProposalForm from '../WcProposalForm' import WcConnectionState from '../WcConnectionState' +import { trackEvent } from '@/services/analytics' +import { WALLETCONNECT_EVENTS } from '@/services/analytics/events/walletconnect' type WcSessionManagerProps = { sessions: SessionTypes.Struct[] @@ -27,6 +29,9 @@ const WcSessionManager = ({ sessions, uri }: WcSessionManagerProps) => { const onApprove = useCallback(async () => { if (!walletConnect || !chainId || !safeAddress || !proposal) return + const label = proposal?.params.proposer.metadata.url + trackEvent({ ...WALLETCONNECT_EVENTS.APPROVE_CLICK, label }) + try { await walletConnect.approveSession(proposal, chainId, safeAddress) } catch (e) { @@ -34,6 +39,8 @@ const WcSessionManager = ({ sessions, uri }: WcSessionManagerProps) => { return } + trackEvent({ ...WALLETCONNECT_EVENTS.CONNECTED, label }) + setProposal(undefined) }, [walletConnect, setError, chainId, safeAddress, proposal]) @@ -41,6 +48,9 @@ const WcSessionManager = ({ sessions, uri }: WcSessionManagerProps) => { const onReject = useCallback(async () => { if (!walletConnect || !proposal) return + const label = proposal?.params.proposer.metadata.url + trackEvent({ ...WALLETCONNECT_EVENTS.REJECT_CLICK, label }) + try { await walletConnect.rejectSession(proposal) } catch (e) { @@ -54,6 +64,9 @@ const WcSessionManager = ({ sessions, uri }: WcSessionManagerProps) => { // On session disconnect const onDisconnect = useCallback( async (session: SessionTypes.Struct) => { + const label = session.peer.metadata.url + trackEvent({ ...WALLETCONNECT_EVENTS.DISCONNECT_CLICK, label }) + if (!walletConnect) return try { await walletConnect.disconnectSession(session) diff --git a/src/services/analytics/events/walletconnect.ts b/src/services/analytics/events/walletconnect.ts new file mode 100644 index 0000000000..3c1e29bf93 --- /dev/null +++ b/src/services/analytics/events/walletconnect.ts @@ -0,0 +1,48 @@ +import { EventType } from '@/services/analytics/types' + +const WALLETCONNECT_CATEGORY = 'walletconnect' + +export const WALLETCONNECT_EVENTS = { + CONNECTED: { + action: 'WC connected', + category: WALLETCONNECT_CATEGORY, + event: EventType.META, + }, + POPUP_OPENED: { + action: 'WC popup', + category: WALLETCONNECT_CATEGORY, + }, + DISCONNECT_CLICK: { + action: 'WC disconnect click', + category: WALLETCONNECT_CATEGORY, + }, + APPROVE_CLICK: { + action: 'WC approve click', + category: WALLETCONNECT_CATEGORY, + }, + REJECT_CLICK: { + action: 'WC reject click', + category: WALLETCONNECT_CATEGORY, + }, + PASTE_CLICK: { + action: 'WC paste click', + category: WALLETCONNECT_CATEGORY, + }, + HINTS_SHOW: { + action: 'WC show hints', + category: WALLETCONNECT_CATEGORY, + }, + HINTS_EXPAND: { + action: 'WC expand hints', + category: WALLETCONNECT_CATEGORY, + }, + SHOW_RISK: { + action: 'WC show risk', + category: WALLETCONNECT_CATEGORY, + event: EventType.META, + }, + ACCEPT_RISK: { + action: 'WC accept risk', + category: WALLETCONNECT_CATEGORY, + }, +}