Repository: MetaMask/metamask-mobile
Pull request #14246: feat: add metrics for Alert System
Author: login: vinistevam
Labels: name: team-confirmations
description: Push issues to confirmations team
name: release-7.45.0
description: Issue or pull request that will be included in release 7.45.0
Created: 2025-03-26T11:23:34Z | Merged: 2025-04-07T05:48:43Z

<!--
Please submit this PR as a draft initially.
Do not mark it as "Ready for review" until the template has been completely filled out, and PR status checks have passed at least once.
-->

## **Description**
This PR aims to implement metrics to the alert system and use them in signature requests that use the alert system. It's not included in transactions in this PR.

```
alert_triggered_count: total number of alerts triggered within that confirmation
alert_triggered (list): name of the alerts displayed, e.g. insufficient_funds_for_gas, pending_transaction
alert_visualized_count: total number of alerts visualized by the user within that confirmation
alert_visualized (list): name of the alerts that the user visualized regardless if by clicking on the key or going through it after clicking confirm
alert_resolved_count (list): total number of alerts that were triggered but then resolved before the user confirmed or rejected the request
alert_resolved (list): name of alerts that were triggered but then resolved before the user confirmed
```


<!--
Write a short description of the changes included in this pull request, also include relevant motivation and context. Have in mind the following questions:
1. What is the reason for the change?
2. What is the improvement/solution?
-->

## **Related issues**

Fixes: https://github.com/MetaMask/MetaMask-planning/issues/3923

## **Manual testing steps**

**Confim case**
1. Go to this the test dapp
2. click on `Sign In With Ethereum (Bad Domain)`
3. Click on the inline alert
4. Close
5. Click on `Review alert`
6. Acknowledge the alert
7. Acknowledge the Friction modal and confirm the request

**Reject case**
1. Go to this the test dapp
2. click on `Sign In With Ethereum (Bad Domain)`
3. Click on the inline alert
6. Acknowledge the alert
7. Cancel the request

**Blockaid Alert**
1. Go to this the test dapp
2. click on `Malicious Permit` in the Signature and Blockaid section
4. Reject or Confirm the request

Dev env we can see the events via the terminal in the build we have to go to Segment to check the events.

## **Screenshots/Recordings**

[alert_metrics.webm](https://github.com/user-attachments/assets/59a8b208-520b-4248-8976-9d280c00a96f)

<!-- If applicable, add screenshots and/or recordings to visualize the before and after of your change. -->

### **Before**

<!-- [screenshots/recordings] -->

### **After**

<!-- [screenshots/recordings] -->

## **Pre-merge author checklist**

- [x] I’ve followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile Coding Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md).
- [x] I've completed the PR template to the best of my ability
- [x] I’ve included tests if applicable
- [x] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable
- [x] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors.

## **Pre-merge reviewer checklist**

- [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed).
- [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots.



Diff:
diff --git a/app/components/Views/confirmations/AlertSystem/AlertModal/AlertModal.test.tsx b/app/components/Views/confirmations/AlertSystem/AlertModal/AlertModal.test.tsx
index e1a00d2ffe7..d3fc8a4f927 100644
--- a/app/components/Views/confirmations/AlertSystem/AlertModal/AlertModal.test.tsx
+++ b/app/components/Views/confirmations/AlertSystem/AlertModal/AlertModal.test.tsx
@@ -5,11 +5,16 @@ import AlertModal from './AlertModal';
 import { IconName } from '../../../../../component-library/components/Icons/Icon';
 import Text from '../../../../../component-library/components/Texts/Text';
 import { Severity } from '../../types/alerts';
+import { useConfirmationAlertMetrics } from '../../hooks/useConfirmationAlertMetrics';
 
 jest.mock('../context', () => ({
   useAlerts: jest.fn(),
 }));
 
+jest.mock('../../hooks/useConfirmationAlertMetrics', () => ({
+  useConfirmationAlertMetrics: jest.fn(),
+}));
+
 const ALERT_MESSAGE_MOCK = 'This is a test alert message.';
 const ALERT_DETAILS_MOCK = ['Detail 1', 'Detail 2'];
 const ALERT_ACTION_MOCK = { label: 'Action 1', callback: jest.fn() };
@@ -59,9 +64,13 @@ describe('AlertModal', () => {
     hasUnconfirmedDangerAlerts: false,
     hasUnconfirmedFieldDangerAlerts: false,
   };
+  const mockTrackAlertRendered = jest.fn();
 
   beforeEach(() => {
     (useAlerts as jest.Mock).mockReturnValue(baseMockUseAlerts);
+    (useConfirmationAlertMetrics as jest.Mock).mockReturnValue({
+      trackAlertRendered: mockTrackAlertRendered,
+    });
     jest.clearAllMocks();
   });
 
@@ -235,4 +244,18 @@ describe('AlertModal', () => {
     const { queryByText } = render(<AlertModal />);
     expect(queryByText(ALERT_MESSAGE_MOCK)).toBeNull();
   });
+
+  it('calls trackAlertRendered when modal is rendered', () => {
+    render(<AlertModal />);
+    expect(mockTrackAlertRendered).toHaveBeenCalled();
+  });
+
+  it('does not call trackAlertRendered when modal is not visible', () => {
+    (useAlerts as jest.Mock).mockReturnValue({
+      ...baseMockUseAlerts,
+      alertModalVisible: false,
+    });
+    render(<AlertModal />);
+    expect(mockTrackAlertRendered).not.toHaveBeenCalled();
+  });
 });
diff --git a/app/components/Views/confirmations/AlertSystem/AlertModal/AlertModal.tsx b/app/components/Views/confirmations/AlertSystem/AlertModal/AlertModal.tsx
index a6f7bbf4c16..577df105ad3 100755
--- a/app/components/Views/confirmations/AlertSystem/AlertModal/AlertModal.tsx
+++ b/app/components/Views/confirmations/AlertSystem/AlertModal/AlertModal.tsx
@@ -1,17 +1,18 @@
-import React, { useCallback } from 'react';
+import React, { useCallback, useEffect } from 'react';
 import { TouchableOpacity, View, ViewStyle } from 'react-native';
-import { useTheme } from '../../../../../util/theme';
-import { useAlerts } from '../context';
 import BottomModal from '../../components/UI/BottomModal';
 import Button, { ButtonSize, ButtonVariants, ButtonWidthTypes } from '../../../../../component-library/components/Buttons/Button';
 import Checkbox from '../../../../../component-library/components/Checkbox';
 import Icon, { IconName, IconSize } from '../../../../../component-library/components/Icons/Icon';
 import Text, { TextVariant } from '../../../../../component-library/components/Texts/Text';
-import { useStyles } from '../../../../hooks/useStyles';
-import styleSheet from './AlertModal.styles';
-import { strings } from '../../../../../../locales/i18n';
 import { Alert, Severity } from '../../types/alerts';
 import { getSeverityStyle } from '../utils';
+import { strings } from '../../../../../../locales/i18n';
+import { useAlerts } from '../context';
+import { useConfirmationAlertMetrics } from '../../hooks/useConfirmationAlertMetrics';
+import { useStyles } from '../../../../hooks/useStyles';
+import { useTheme } from '../../../../../util/theme';
+import styleSheet from './AlertModal.styles';
 
 interface HeaderProps {
   iconColor: string;
@@ -136,6 +137,13 @@ const AlertModal: React.FC<AlertModalProps> = ({ headerAccessory, onAcknowledgeC
   const { colors } = useTheme();
   const styles = (useStyles(styleSheet, {})).styles as Record<string, ViewStyle>;
   const { hideAlertModal, alertModalVisible, fieldAlerts, alertKey, isAlertConfirmed, setAlertConfirmed } = useAlerts();
+  const { trackAlertRendered } = useConfirmationAlertMetrics();
+
+  useEffect(() => {
+    if (alertModalVisible) {
+      trackAlertRendered();
+    }
+  }, [alertModalVisible, trackAlertRendered]);
 
   const handleClose = useCallback(
     () => {
diff --git a/app/components/Views/confirmations/AlertSystem/MultipleAlertModal/MultipleAlertModal.test.tsx b/app/components/Views/confirmations/AlertSystem/MultipleAlertModal/MultipleAlertModal.test.tsx
index 3e821f4cd68..f989a7c3d20 100644
--- a/app/components/Views/confirmations/AlertSystem/MultipleAlertModal/MultipleAlertModal.test.tsx
+++ b/app/components/Views/confirmations/AlertSystem/MultipleAlertModal/MultipleAlertModal.test.tsx
@@ -3,11 +3,16 @@ import { render, fireEvent, act } from '@testing-library/react-native';
 import { useAlerts } from '../context';
 import MultipleAlertModal from './MultipleAlertModal';
 import { Severity } from '../../types/alerts';
+import { useConfirmationAlertMetrics } from '../../hooks/useConfirmationAlertMetrics';
 
 jest.mock('../context', () => ({
   useAlerts: jest.fn(),
 }));
 
+jest.mock('../../hooks/useConfirmationAlertMetrics', () => ({
+  useConfirmationAlertMetrics: jest.fn(),
+}));
+
 const ALERT_MESSAGE_MOCK = 'This is a test alert message.';
 const ALERT_DETAILS_MOCK = ['Detail 1', 'Detail 2'];
 const ALERT_ACTION_MOCK = { label: 'Action 1', callback: jest.fn() };
@@ -55,9 +60,13 @@ describe('MultipleAlertModal', () => {
     hasUnconfirmedDangerAlerts: false,
     hasUnconfirmedFieldDangerAlerts: false,
   };
+  const trackAlertRendered = jest.fn();
 
   beforeEach(() => {
     (useAlerts as jest.Mock).mockReturnValue(baseMockUseAlerts);
+    (useConfirmationAlertMetrics as jest.Mock).mockReturnValue({
+      trackAlertRendered,
+    });
     jest.clearAllMocks();
   });
 
diff --git a/app/components/Views/confirmations/AlertSystem/context/Alerts.context.test.tsx b/app/components/Views/confirmations/AlertSystem/context/Alerts.context.test.tsx
index 3b8987b21c7..1826e10ab9d 100644
--- a/app/components/Views/confirmations/AlertSystem/context/Alerts.context.test.tsx
+++ b/app/components/Views/confirmations/AlertSystem/context/Alerts.context.test.tsx
@@ -4,6 +4,12 @@ import { Severity, Alert } from '../../types/alerts';
 import { useAlerts, AlertsContextProvider, AlertsContextParams } from './Alerts.context';
 import { useAlertsConfirmed } from '../../../../hooks/useAlertsConfirmed';
 
+jest.mock('react-redux', () => ({
+  ...jest.requireActual('react-redux'),
+  useSelector: jest.fn(),
+  useDispatch: jest.fn(),
+}));
+
 jest.mock('../../hooks/useConfirmationAlerts', () => ({
   __esModule: true,
   default: jest.fn(),
diff --git a/app/components/Views/confirmations/components/Confirm/Footer/Footer.test.tsx b/app/components/Views/confirmations/components/Confirm/Footer/Footer.test.tsx
index fe6fd3d82ed..03f2db4eedf 100644
--- a/app/components/Views/confirmations/components/Confirm/Footer/Footer.test.tsx
+++ b/app/components/Views/confirmations/components/Confirm/Footer/Footer.test.tsx
@@ -39,6 +39,18 @@ jest.mock('../../../../../hooks/useAlertsConfirmed', () => ({
   useAlertsConfirmed: jest.fn(),
 }));
 
+jest.mock('../../../hooks/useConfirmationAlertMetrics', () => ({
+  useConfirmationAlertMetrics: jest.fn(),
+}));
+
+import { useConfirmationAlertMetrics } from '../../../hooks/useConfirmationAlertMetrics';
+
+const mockTrackAlertMetrics = jest.fn();
+
+(useConfirmationAlertMetrics as jest.Mock).mockReturnValue({
+  trackAlertMetrics: mockTrackAlertMetrics,
+});
+
 const ALERT_MESSAGE_MOCK = 'This is a test alert message.';
 const ALERT_DETAILS_MOCK = ['Detail 1', 'Detail 2'];
 const mockAlerts = [
@@ -53,8 +65,8 @@ const mockAlerts = [
 
 describe('Footer', () => {
   beforeEach(() => {
-    (useAlerts as jest.Mock).mockReturnValue({fieldAlerts: [], hasDangerAlerts: false,});
-    (useAlertsConfirmed as jest.Mock).mockReturnValue({hasUnconfirmedDangerAlerts: false,});
+    (useAlerts as jest.Mock).mockReturnValue({ fieldAlerts: [], hasDangerAlerts: false, });
+    (useAlertsConfirmed as jest.Mock).mockReturnValue({ hasUnconfirmedDangerAlerts: false, });
     jest.clearAllMocks();
   });
 
@@ -230,5 +242,12 @@ describe('Footer', () => {
 
       expect(getByText(expectedText)).toBeDefined();
     });
+
+    it('calls trackAlertMetrics when alerts change', () => {
+      renderWithProvider(<Footer />, {
+        state: personalSignatureConfirmationState,
+      });
+      expect(mockTrackAlertMetrics).toHaveBeenCalledTimes(1);
+    });
   });
 });
diff --git a/app/components/Views/confirmations/components/Confirm/Footer/Footer.tsx b/app/components/Views/confirmations/components/Confirm/Footer/Footer.tsx
index ba39ee9b2c6..d94fe3663cd 100644
--- a/app/components/Views/confirmations/components/Confirm/Footer/Footer.tsx
+++ b/app/components/Views/confirmations/components/Confirm/Footer/Footer.tsx
@@ -1,4 +1,4 @@
-import React, { useCallback, useState } from 'react';
+import React, { useCallback, useEffect, useState } from 'react';
 import { Linking, View } from 'react-native';
 import { ConfirmationFooterSelectorIDs } from '../../../../../../../e2e/selectors/Confirmation/ConfirmationView.selectors';
 import { strings } from '../../../../../../../locales/i18n';
@@ -18,6 +18,7 @@ import { useAlerts } from '../../../AlertSystem/context';
 import ConfirmAlertModal from '../../../AlertSystem/ConfirmAlertModal';
 import { useLedgerContext } from '../../../context/LedgerContext';
 import { useConfirmActions } from '../../../hooks/useConfirmActions';
+import { useConfirmationAlertMetrics } from '../../../hooks/useConfirmationAlertMetrics';
 import { useQRHardwareContext } from '../../../context/QRHardwareContext/QRHardwareContext';
 import { useSecurityAlertResponse } from '../../../hooks/useSecurityAlertResponse';
 import { useTransactionMetadataRequest } from '../../../hooks/useTransactionMetadataRequest';
@@ -26,7 +27,7 @@ import { ResultType } from '../../../constants/signatures';
 import styleSheet from './Footer.styles';
 
 export const Footer = () => {
-  const { fieldAlerts, hasDangerAlerts, hasUnconfirmedDangerAlerts } = useAlerts();
+  const { alerts, fieldAlerts, hasDangerAlerts, hasUnconfirmedDangerAlerts } = useAlerts();
   const { onConfirm, onReject } = useConfirmActions();
   const { isQRSigningInProgress, needsCameraPermission } =
     useQRHardwareContext();
@@ -34,6 +35,7 @@ export const Footer = () => {
   const { isLedgerAccount } = useLedgerContext();
   const confirmDisabled = needsCameraPermission;
   const transactionMetadata = useTransactionMetadataRequest();
+  const { trackAlertMetrics } = useConfirmationAlertMetrics();
 
   const isStakingConfirmationBool = isStakingConfirmation(
     transactionMetadata?.type as string,
@@ -68,6 +70,10 @@ export const Footer = () => {
     await onConfirm();
   }, [hasDangerAlerts, onConfirm, showConfirmAlertModal]);
 
+  useEffect(() => {
+    trackAlertMetrics();
+  }, [alerts, trackAlertMetrics]);
+
   const { styles } = useStyles(styleSheet, {
     confirmDisabled,
     isStakingConfirmationBool,
diff --git a/app/components/Views/confirmations/components/Confirm/Info/Shared/InfoRowOrigin/InfoRowOrigin.tsx b/app/components/Views/confirmations/components/Confirm/Info/Shared/InfoRowOrigin/InfoRowOrigin.tsx
index bb094efc3e3..b3ec1aa75f6 100644
--- a/app/components/Views/confirmations/components/Confirm/Info/Shared/InfoRowOrigin/InfoRowOrigin.tsx
+++ b/app/components/Views/confirmations/components/Confirm/Info/Shared/InfoRowOrigin/InfoRowOrigin.tsx
@@ -29,7 +29,7 @@ const InfoRowOrigin = () => {
       testID={ConfirmationPageSectionsSelectorIDs.ORIGIN_INFO_SECTION}
     >
       <AlertRow
-        alertKey={RowAlertKey.RequestFrom}
+        alertField={RowAlertKey.RequestFrom}
         label={strings('confirm.label.request_from')}
         tooltip={strings('confirm.personal_sign_tooltip')}
       >
diff --git a/app/components/Views/confirmations/components/UI/InfoRow/AlertRow/AlertRow.test.tsx b/app/components/Views/confirmations/components/UI/InfoRow/AlertRow/AlertRow.test.tsx
index d556560bdaa..d98b696c09e 100755
--- a/app/components/Views/confirmations/components/UI/InfoRow/AlertRow/AlertRow.test.tsx
+++ b/app/components/Views/confirmations/components/UI/InfoRow/AlertRow/AlertRow.test.tsx
@@ -4,35 +4,48 @@ import { useAlerts } from '../../../../AlertSystem/context';
 import AlertRow, { AlertRowProps } from './AlertRow';
 import { Severity } from '../../../../types/alerts';
 import { IconName } from '../../../../../../../component-library/components/Icons/Icon';
+import { useConfirmationAlertMetrics } from '../../../../hooks/useConfirmationAlertMetrics';
+
 
 jest.mock('../../../../AlertSystem/context', () => ({
   useAlerts: jest.fn(),
 }));
 
+jest.mock('../../../../hooks/useConfirmationAlertMetrics', () => ({
+  useConfirmationAlertMetrics: jest.fn(),
+}));
+
 const ALERT_KEY_DANGER = 'DANGER_ALERT';
 const ALERT_KEY_WARNING = 'WARNING_ALERT';
 const ALERT_KEY_INFO = 'INFO_ALERT';
+const ALERT_FIELD_DANGER = 'field_danger';
+const ALERT_FIELD_WARNING = 'field_warning';
+const ALERT_FIELD_INFO = 'field_info';
 const mockAlerts = [
   {
     key: ALERT_KEY_DANGER,
+    field: ALERT_FIELD_DANGER,
     severity: Severity.Danger,
     message: 'This is a danger alert',
   },
   {
     key: ALERT_KEY_WARNING,
+    field: ALERT_FIELD_WARNING,
     severity: Severity.Warning,
     message: 'This is a warning alert',
   },
   {
     key: ALERT_KEY_INFO,
+    field: ALERT_FIELD_INFO,
     severity: Severity.Info,
-    message: 'This is a info alert',
+    message: 'This is an info alert',
   },
 ];
 
 describe('AlertRow', () => {
   const mockShowAlertModal = jest.fn();
   const mockSetAlertKey = jest.fn();
+  const mockTrackInlineAlertClicked = jest.fn();
   const useAlertsBase = {
     alerts: mockAlerts,
     fieldAlerts: mockAlerts,
@@ -43,12 +56,15 @@ describe('AlertRow', () => {
   beforeEach(() => {
     jest.clearAllMocks();
     (useAlerts as jest.Mock).mockReturnValue(useAlertsBase);
+    (useConfirmationAlertMetrics as jest.Mock).mockReturnValue({
+      trackInlineAlertClicked: mockTrackInlineAlertClicked,
+    });
   });
 
   const LABEL_MOCK = 'Alert Label';
   const CHILDREN_MOCK = 'Test Children';
   const baseProps: AlertRowProps = {
-    alertKey: ALERT_KEY_DANGER,
+    alertField: ALERT_FIELD_DANGER,
     label: LABEL_MOCK,
     children: CHILDREN_MOCK,
   };
@@ -63,8 +79,8 @@ describe('AlertRow', () => {
   });
 
   it('renders correctly with warning alert', () => {
-    const props = { ...baseProps, alertKey: ALERT_KEY_WARNING };
-    const { getByText, getByTestId} = render(<AlertRow {...props} />);
+    const props = { ...baseProps, alertField: ALERT_FIELD_WARNING };
+    const { getByText, getByTestId } = render(<AlertRow {...props} />);
     expect(getByText(LABEL_MOCK)).toBeDefined();
     expect(getByText(CHILDREN_MOCK)).toBeDefined();
     const icon = getByTestId('inline-alert-icon');
@@ -72,7 +88,7 @@ describe('AlertRow', () => {
   });
 
   it('renders correctly with default alert', () => {
-    const props = { ...baseProps, alertKey: ALERT_KEY_INFO };
+    const props = { ...baseProps, alertField: ALERT_FIELD_INFO };
     const { getByText, getByTestId } = render(<AlertRow {...props} />);
     expect(getByText(LABEL_MOCK)).toBeDefined();
     expect(getByText(CHILDREN_MOCK)).toBeDefined();
@@ -81,7 +97,7 @@ describe('AlertRow', () => {
   });
 
   it('does not render when isShownWithAlertsOnly is true and no alert is present', () => {
-    const props = { ...baseProps, alertKey: 'alert4', isShownWithAlertsOnly: true };
+    const props = { ...baseProps, alertField: 'alert4', isShownWithAlertsOnly: true };
     const { queryByText } = render(<AlertRow {...props} />);
     expect(queryByText(LABEL_MOCK)).toBeNull();
     expect(queryByText(CHILDREN_MOCK)).toBeNull();
@@ -94,11 +110,13 @@ describe('AlertRow', () => {
     expect(getByText(CHILDREN_MOCK)).toBeDefined();
   });
 
-  it('calls showAlertModal and setAlertKey when inline alert is clicked', () => {
-    const { getByText } = render(<AlertRow {...baseProps} />);
-    const inlineAlert = getByText('Alert');
+  it('calls showAlertModal, setAlertKey and trackInlineAlertClicked when inline alert is clicked', () => {
+    const { getByTestId } = render(<AlertRow {...baseProps} />);
+    const inlineAlert = getByTestId('inline-alert');
     fireEvent.press(inlineAlert);
     expect(mockSetAlertKey).toHaveBeenCalledWith(ALERT_KEY_DANGER);
     expect(mockShowAlertModal).toHaveBeenCalled();
+    expect(mockTrackInlineAlertClicked).toHaveBeenCalled();
+    expect(mockTrackInlineAlertClicked).toHaveBeenCalledWith(ALERT_FIELD_DANGER);
   });
 });
diff --git a/app/components/Views/confirmations/components/UI/InfoRow/AlertRow/AlertRow.tsx b/app/components/Views/confirmations/components/UI/InfoRow/AlertRow/AlertRow.tsx
index 4125c8e5c6c..af11c630cfd 100755
--- a/app/components/Views/confirmations/components/UI/InfoRow/AlertRow/AlertRow.tsx
+++ b/app/components/Views/confirmations/components/UI/InfoRow/AlertRow/AlertRow.tsx
@@ -1,8 +1,9 @@
 import React from 'react';
-import { TextColor } from '../../../../../../../component-library/components/Texts/Text';
 import InlineAlert from '../../../../AlertSystem/InlineAlert/InlineAlert';
 import { useAlerts } from '../../../../AlertSystem/context';
+import { useConfirmationAlertMetrics } from '../../../../hooks/useConfirmationAlertMetrics';
 import { Severity } from '../../../../types/alerts';
+import { TextColor } from '../../../../../../../component-library/components/Texts/Text';
 import InfoRow, { InfoRowProps } from '../InfoRow';
 
 function getAlertTextColors(
@@ -19,18 +20,21 @@ function getAlertTextColors(
 }
 
 export interface AlertRowProps extends InfoRowProps {
-  alertKey: string;
+  alertField: string;
   /** Determines whether to display the row only when an alert is present. */
   isShownWithAlertsOnly?: boolean;
 }
 
-const AlertRow = ({ alertKey, isShownWithAlertsOnly, ...props }: AlertRowProps) => {
-  const { alerts, showAlertModal, setAlertKey } = useAlerts();
-  const alertSelected = alerts.find((a) => a.key === alertKey);
+const AlertRow = ({ alertField, isShownWithAlertsOnly, ...props }: AlertRowProps) => {
+  const { fieldAlerts, showAlertModal, setAlertKey } = useAlerts();
+  const { trackInlineAlertClicked } = useConfirmationAlertMetrics();
+  const alertSelected = fieldAlerts.find((a) => a.field === alertField);
 
   const handleInlineAlertClick = () => {
-    setAlertKey(alertKey);
+    if(!alertSelected) return;
+    setAlertKey(alertSelected.key);
     showAlertModal();
+    trackInlineAlertClicked(alertSelected.field);
   };
 
   if (!alertSelected && isShownWithAlertsOnly) {
diff --git a/app/components/Views/confirmations/constants/alerts.ts b/app/components/Views/confirmations/constants/alerts.ts
new file mode 100644
index 00000000000..e89376d73e5
--- /dev/null
+++ b/app/components/Views/confirmations/constants/alerts.ts
@@ -0,0 +1,4 @@
+export enum AlertKeys {
+  Blockaid = 'blockaid',
+  DomainMismatch = 'domain_mismatch',
+}
diff --git a/app/components/Views/confirmations/hooks/alerts/signatures/useDomainMismatchAlerts.test.ts b/app/components/Views/confirmations/hooks/alerts/signatures/useDomainMismatchAlerts.test.ts
index bf8d510c94b..a8e9aa25d0f 100755
--- a/app/components/Views/confirmations/hooks/alerts/signatures/useDomainMismatchAlerts.test.ts
+++ b/app/components/Views/confirmations/hooks/alerts/signatures/useDomainMismatchAlerts.test.ts
@@ -5,6 +5,7 @@ import { isSIWESignatureRequest } from '../../../utils/signature';
 import useDomainMismatchAlerts from './useDomainMismatchAlerts';
 import { renderHookWithProvider } from '../../../../../../util/test/renderWithProvider';
 import { siweSignatureConfirmationState } from '../../../../../../util/test/confirm-data-helpers';
+import { AlertKeys } from '../../../constants/alerts';
 
 jest.mock('@metamask/controller-utils');
 jest.mock('../../../utils/signature');
@@ -12,7 +13,7 @@ jest.mock('../../../utils/signature');
 describe('useDomainMismatchAlerts', () => {
   const ALERT_MOCK = {
     field: RowAlertKey.RequestFrom,
-    key: RowAlertKey.RequestFrom,
+    key: AlertKeys.DomainMismatch,
     message: `The site making the request is not the site you’re signing into. This could be an attempt to steal your login credentials.`,
     title: 'Suspicious sign-in request',
     severity: Severity.Danger,
diff --git a/app/components/Views/confirmations/hooks/alerts/signatures/useDomainMismatchAlerts.ts b/app/components/Views/confirmations/hooks/alerts/signatures/useDomainMismatchAlerts.ts
index 9ccc42bbdfb..34b3a8705b2 100755
--- a/app/components/Views/confirmations/hooks/alerts/signatures/useDomainMismatchAlerts.ts
+++ b/app/components/Views/confirmations/hooks/alerts/signatures/useDomainMismatchAlerts.ts
@@ -7,6 +7,7 @@ import { useSignatureRequest } from '../../useSignatureRequest';
 import { isSIWESignatureRequest } from '../../../utils/signature';
 import { strings } from '../../../../../../../locales/i18n';
 import { regex } from '../../../../../../util/regex';
+import { AlertKeys } from '../../../constants/alerts';
 
 export default function useDomainMismatchAlerts(): Alert[] {
   const { approvalRequest } = useApprovalRequest();
@@ -30,7 +31,7 @@ export default function useDomainMismatchAlerts(): Alert[] {
     return [
       {
         field: RowAlertKey.RequestFrom,
-        key: RowAlertKey.RequestFrom,
+        key: AlertKeys.DomainMismatch,
         message: strings('alert_system.domain_mismatch.message'),
         title: strings('alert_system.domain_mismatch.title'),
         severity: Severity.Danger,
diff --git a/app/components/Views/confirmations/hooks/alerts/useBlockaidAlerts.tsx b/app/components/Views/confirmations/hooks/alerts/useBlockaidAlerts.tsx
index f1922c9275d..de6639f61a1 100755
--- a/app/components/Views/confirmations/hooks/alerts/useBlockaidAlerts.tsx
+++ b/app/components/Views/confirmations/hooks/alerts/useBlockaidAlerts.tsx
@@ -2,7 +2,7 @@ import React, { useCallback, useMemo } from 'react';
 import { strings } from '../../../../../../locales/i18n';
 // TODO: Remove legacy import
 import { Reason, SecurityAlertResponse } from '../../legacy/components/BlockaidBanner/BlockaidBanner.types';
-import { RowAlertKey } from '../../components/UI/InfoRow/AlertRow/constants';
+import { AlertKeys } from '../../constants/alerts';
 import { Alert, AlertSeverity, Severity } from '../../types/alerts';
 import { getAnalyticsParams } from '../../../../../util/confirmation/signatureUtils';
 import { MetaMetricsEvents, useMetrics } from '../../../../hooks/useMetrics';
@@ -79,7 +79,7 @@ export default function useBlockaidAlerts(): Alert[] {
 
     return [
       {
-        key: RowAlertKey.Blockaid,
+        key: AlertKeys.Blockaid,
         content: (
           <BlockaidAlertContent
             alertDetails={features}
diff --git a/app/components/Views/confirmations/hooks/useConfirmationAlertMetrics.test.ts b/app/components/Views/confirmations/hooks/useConfirmationAlertMetrics.test.ts
new file mode 100644
index 00000000000..5e801d98254
--- /dev/null
+++ b/app/components/Views/confirmations/hooks/useConfirmationAlertMetrics.test.ts
@@ -0,0 +1,220 @@
+import { renderHook } from '@testing-library/react-hooks';
+import { useSelector } from 'react-redux';
+import { useAlerts } from '../AlertSystem/context';
+import { useConfirmationMetricEvents } from './useConfirmationMetricEvents';
+import { useConfirmationAlertMetrics } from './useConfirmationAlertMetrics';
+import { AlertKeys } from '../constants/alerts';
+
+jest.mock('react-redux', () => ({
+  useSelector: jest.fn(),
+}));
+
+jest.mock('../AlertSystem/context', () => ({
+  useAlerts: jest.fn(),
+}));
+
+jest.mock('./useConfirmationMetricEvents', () => ({
+  useConfirmationMetricEvents: jest.fn(),
+}));
+
+describe('useConfirmationAlertMetrics', () => {
+  const ALERT_FIELD_FROM_MOCK = 'from';
+  const mockSetConfirmationMetric = jest.fn();
+  const mockUseAlerts = {
+    alerts: [
+      { key: AlertKeys.Blockaid },
+      { key: AlertKeys.DomainMismatch },
+    ],
+    isAlertConfirmed: jest.fn(),
+    alertKey: AlertKeys.DomainMismatch,
+  };
+
+  beforeEach(() => {
+    jest.clearAllMocks();
+    (useConfirmationMetricEvents as jest.Mock).mockReturnValue({
+      setConfirmationMetric: mockSetConfirmationMetric,
+    });
+    (useAlerts as jest.Mock).mockReturnValue(mockUseAlerts);
+  });
+
+  const baseAlertProperties = {
+    alert_trigger_count: 2,
+    alert_trigger_name: [AlertKeys.Blockaid, AlertKeys.DomainMismatch],
+    alert_resolved_count: 0,
+    alert_resolved: [],
+  };
+
+  it('tracks inline alert clicked', () => {
+    (useSelector as jest.Mock).mockReturnValue({
+      properties: {
+        ...baseAlertProperties,
+        alert_visualized: [],
+      },
+    });
+
+    const { result } = renderHook(() => useConfirmationAlertMetrics());
+
+    result.current.trackInlineAlertClicked(ALERT_FIELD_FROM_MOCK);
+
+    expect(mockSetConfirmationMetric).toHaveBeenCalledWith({
+      properties: {
+        ...baseAlertProperties,
+        alert_key_clicked: [AlertKeys.DomainMismatch],
+        alert_field_clicked: [ALERT_FIELD_FROM_MOCK],
+      },
+    });
+  });
+
+  it('tracks alert rendered', () => {
+    (useSelector as jest.Mock).mockReturnValue({
+      properties: {
+        ...baseAlertProperties,
+      },
+    });
+
+    const { result } = renderHook(() => useConfirmationAlertMetrics());
+
+    result.current.trackAlertRendered();
+
+    expect(mockSetConfirmationMetric).toHaveBeenCalledWith({
+      properties: {
+        ...baseAlertProperties,
+        alert_visualized: [AlertKeys.DomainMismatch],
+        alert_visualized_count: 1,
+      },
+    });
+  });
+
+  it('handles confirmed alerts correctly', () => {
+    (mockUseAlerts.isAlertConfirmed as jest.Mock).mockImplementation((key: string) => key === AlertKeys.Blockaid);
+
+    (useSelector as jest.Mock).mockReturnValue({
+      properties: baseAlertProperties,
+    });
+
+    const { result } = renderHook(() => useConfirmationAlertMetrics());
+
+    result.current.trackAlertMetrics();
+
+    expect(mockSetConfirmationMetric).toHaveBeenCalledWith({
+      properties: {
+        ...baseAlertProperties,
+        alert_resolved_count: 1,
+        alert_resolved: [AlertKeys.Blockaid],
+      },
+    });
+  });
+
+  it('uses alertKey when there is no match in ALERTS_NAME_METRICS', () => {
+    const UNKNOWN_ALERT_KEY_MOCK = 'unknown_alert';
+    const mockUseAlertsWithUnknownKey = {
+      alerts: [
+        { key: UNKNOWN_ALERT_KEY_MOCK },
+      ],
+      isAlertConfirmed: jest.fn(),
+      alertKey: UNKNOWN_ALERT_KEY_MOCK,
+    };
+
+    (useAlerts as jest.Mock).mockReturnValue(mockUseAlertsWithUnknownKey);
+    (useSelector as jest.Mock).mockReturnValue({
+      properties: {
+        alert_trigger_count: 1,
+        alert_trigger_name: [UNKNOWN_ALERT_KEY_MOCK],
+        alert_resolved_count: 0,
+        alert_resolved: [],
+        alert_visualized: [],
+      },
+    });
+
+    const { result } = renderHook(() => useConfirmationAlertMetrics());
+
+    result.current.trackInlineAlertClicked(ALERT_FIELD_FROM_MOCK);
+
+    expect(mockSetConfirmationMetric).toHaveBeenCalledTimes(1);
+    expect(mockSetConfirmationMetric).toHaveBeenCalledWith({
+      properties: {
+        alert_trigger_count: 1,
+        alert_trigger_name: [UNKNOWN_ALERT_KEY_MOCK],
+        alert_resolved_count: 0,
+        alert_resolved: [],
+        alert_key_clicked: [UNKNOWN_ALERT_KEY_MOCK],
+        alert_field_clicked: [ALERT_FIELD_FROM_MOCK],
+      },
+    });
+  });
+
+  it('handles undefined alert_visualized', () => {
+    (mockUseAlerts.isAlertConfirmed as jest.Mock).mockReturnValue(false);
+    (useSelector as jest.Mock).mockReturnValue({
+      properties: {
+        ...baseAlertProperties,
+        alert_visualized: undefined,
+      },
+    });
+
+    const { result } = renderHook(() => useConfirmationAlertMetrics());
+
+    result.current.trackAlertRendered();
+
+    expect(mockSetConfirmationMetric).toHaveBeenCalledWith({
+      properties: {
+        ...baseAlertProperties,
+        alert_visualized: [AlertKeys.DomainMismatch],
+        alert_visualized_count: 1,
+      },
+    });
+  });
+
+  it('handles undefined alert_key', () => {
+    (mockUseAlerts.isAlertConfirmed as jest.Mock).mockReturnValue(false);
+    (useSelector as jest.Mock).mockReturnValue({
+      properties: {
+        ...baseAlertProperties,
+        alert_key_clicked: undefined,
+      },
+    });
+
+    const { result } = renderHook(() => useConfirmationAlertMetrics());
+
+    result.current.trackInlineAlertClicked();
+
+    expect(mockSetConfirmationMetric).toHaveBeenCalledWith({
+      properties: {
+        ...baseAlertProperties,
+        alert_key_clicked: [AlertKeys.DomainMismatch],
+        alert_field_clicked: [],
+      },
+    });
+  });
+
+  it('handles undefined properties in selectConfirmationMetricsById', () => {
+    (mockUseAlerts.isAlertConfirmed as jest.Mock).mockReturnValue(false);
+    (useSelector as jest.Mock).mockReturnValue({});
+
+    const { result } = renderHook(() => useConfirmationAlertMetrics());
+
+    result.current.trackInlineAlertClicked();
+
+    expect(mockSetConfirmationMetric).toHaveBeenCalledWith({
+      properties: {
+        ...baseAlertProperties,
+        alert_key_clicked: [AlertKeys.DomainMismatch],
+        alert_field_clicked: [],
+      },
+    });
+  });
+
+  it('tracks alert metrics', () => {
+    (useSelector as jest.Mock).mockReturnValue({
+      properties: baseAlertProperties,
+    });
+
+    const { result } = renderHook(() => useConfirmationAlertMetrics());
+
+    result.current.trackAlertMetrics();
+
+    expect(mockSetConfirmationMetric).toHaveBeenCalledWith({
+      properties: baseAlertProperties,
+    });
+  });
+});
diff --git a/app/components/Views/confirmations/hooks/useConfirmationAlertMetrics.ts b/app/components/Views/confirmations/hooks/useConfirmationAlertMetrics.ts
new file mode 100644
index 00000000000..b408d47c522
--- /dev/null
+++ b/app/components/Views/confirmations/hooks/useConfirmationAlertMetrics.ts
@@ -0,0 +1,105 @@
+import { useMemo, useCallback } from 'react';
+import { useSelector } from 'react-redux';
+import {
+  selectConfirmationMetricsById,
+} from '../../../../core/redux/slices/confirmationMetrics';
+import { useAlerts } from '../AlertSystem/context';
+import { useConfirmationMetricEvents } from './useConfirmationMetricEvents';
+import { Alert } from '../types/alerts';
+import { RootState } from '../../../../reducers';
+import { useSignatureRequest } from './useSignatureRequest';
+import { AlertKeys } from '../constants/alerts';
+
+type AlertNameMetrics = {
+  [K in AlertKeys]: string;
+};
+
+export function useConfirmationAlertMetrics() {
+  const { setConfirmationMetric } = useConfirmationMetricEvents();
+  const { alerts, isAlertConfirmed, alertKey } = useAlerts();
+  const signatureRequest = useSignatureRequest();
+
+  const alertProperties = useMemo(() => alerts.length > 0 ? {
+    alert_trigger_count: alerts.length,
+    alert_trigger_name: getAlertNames(alerts),
+    alert_resolved_count: alerts.filter((alertSelected) =>
+      isAlertConfirmed(alertSelected.key),
+    ).length,
+    alert_resolved: getAlertNames(
+      alerts.filter((alertSelected) => isAlertConfirmed(alertSelected.key)),
+    ),
+  } : undefined, [alerts, isAlertConfirmed]);
+
+  const confirmationMetrics = useSelector((state: RootState) =>
+    selectConfirmationMetricsById(state, signatureRequest?.id ?? '')
+  );
+
+    const trackInlineAlertClicked = useCallback((alertField?: string) => {
+      const alertKeyClicked = uniqueFreshArrayPush(
+        (confirmationMetrics?.properties?.alert_key_clicked as string[]) ?? [],
+        getAlertName(alertKey)
+      );
+      const alertFieldClickedMetrics = confirmationMetrics?.properties?.alert_field_clicked as string[] ?? [];
+      const alertFieldClicked = alertField ? uniqueFreshArrayPush(
+        alertFieldClickedMetrics,
+        alertField) : alertFieldClickedMetrics;
+
+      setConfirmationMetric({
+        properties: {
+          ...alertProperties,
+          alert_key_clicked: alertKeyClicked,
+          alert_field_clicked: alertFieldClicked,
+        },
+      });
+    }, [confirmationMetrics, alertKey, setConfirmationMetric, alertProperties]);
+
+    const trackAlertRendered = useCallback(() => {
+      const alertVisualized = uniqueFreshArrayPush(
+        (confirmationMetrics?.properties?.alert_visualized as string[]) ?? [],
+        getAlertName(alertKey)
+      );
+
+      setConfirmationMetric({
+        properties: {
+          ...alertProperties,
+          alert_visualized: alertVisualized,
+          alert_visualized_count: alertVisualized.length,
+        },
+      });
+  }, [confirmationMetrics, alertKey, setConfirmationMetric, alertProperties]);
+
+    const trackAlertMetrics = useCallback(() => {
+      if (!alertProperties) {
+        return;
+      }
+      setConfirmationMetric({
+        properties: {
+          ...alertProperties,
+        },
+      });
+
+    }, [alertProperties, setConfirmationMetric]);
+
+  return {
+    trackAlertRendered,
+    trackInlineAlertClicked,
+    trackAlertMetrics,
+  };
+}
+
+function uniqueFreshArrayPush<T>(array: T[], value: T): T[] {
+  return [...new Set([...array, value])];
+}
+
+function getAlertNames(alerts: Alert[]): string[] {
+  return alerts.map((alertSelected) => getAlertName(alertSelected.key));
+}
+
+const ALERTS_NAME_METRICS: AlertNameMetrics = {
+  [AlertKeys.Blockaid]: 'blockaid',
+  [AlertKeys.DomainMismatch]: 'domain_mismatch',
+};
+
+function getAlertName(alertKey: string): string {
+  return ALERTS_NAME_METRICS[alertKey as AlertKeys] ?? alertKey;
+}
diff --git a/app/components/Views/confirmations/hooks/useSignatureMetrics.ts b/app/components/Views/confirmations/hooks/useSignatureMetrics.ts
index e4e370b51b9..dea5cc0aee2 100644
--- a/app/components/Views/confirmations/hooks/useSignatureMetrics.ts
+++ b/app/components/Views/confirmations/hooks/useSignatureMetrics.ts
@@ -15,6 +15,9 @@ import { useSignatureRequest } from './useSignatureRequest';
 import { useSecurityAlertResponse } from './useSecurityAlertResponse';
 import { useTypedSignSimulationEnabled } from './useTypedSignSimulationEnabled';
 import { parseTypedDataMessageFromSignatureRequest } from '../utils/signature';
+import { useSelector } from 'react-redux';
+import { selectConfirmationMetricsById } from '../../../../core/redux/slices/confirmationMetrics';
+import { RootState } from '../../../../reducers';
 
 interface MessageParamsType {
   meta: Record<string, unknown>;
@@ -32,6 +35,7 @@ const getAnalyticsParams = (
   decodingLoading: boolean,
   isSimulationEnabled: boolean,
   primaryType: string,
+  confirmationMetrics: Record<string, unknown>,
 ) => {
   const { meta = {}, from, version } = messageParams;
 
@@ -52,6 +56,7 @@ const getAnalyticsParams = (
       decodingLoading,
       isSimulationEnabled,
     ),
+    ...confirmationMetrics,
   };
 };
 
@@ -60,15 +65,18 @@ export const useSignatureMetrics = () => {
   const isSimulationEnabled = useTypedSignSimulationEnabled();
   const { securityAlertResponse } = useSecurityAlertResponse();
 
-  const { chainId, decodingData, decodingLoading, messageParams, type } =
+  const { chainId, decodingData, decodingLoading, messageParams, type, id } =
     signatureRequest ?? {};
   const { primaryType } = parseTypedDataMessageFromSignatureRequest(signatureRequest) ?? {};
 
+  const confirmationMetrics = useSelector((state: RootState) =>
+    selectConfirmationMetricsById(state, id ?? '')
+  );
+
   const analyticsParams = useMemo(() => {
     if (!type || !isSignatureRequest(type)) {
       return;
     }
-
     return getAnalyticsParams(
       messageParams as unknown as MessageParamsType,
       securityAlertResponse as SecurityAlertResponse,
@@ -78,17 +86,9 @@ export const useSignatureMetrics = () => {
       !!decodingLoading,
       !!isSimulationEnabled,
       primaryType,
+      confirmationMetrics?.properties ?? {},
     );
-  }, [
-    chainId,
-    decodingData,
-    decodingLoading,
-    isSimulationEnabled,
-    messageParams,
-    primaryType,
-    securityAlertResponse,
-    type,
-  ]);
+  }, [chainId, confirmationMetrics, decodingData, decodingLoading, isSimulationEnabled, messageParams, primaryType, securityAlertResponse, type]);
 
   const captureSignatureMetrics = useCallback(
     async (
diff --git a/app/core/redux/slices/confirmationMetrics/index.test.ts b/app/core/redux/slices/confirmationMetrics/index.test.ts
index d09bcf6873e..59d825d081e 100644
--- a/app/core/redux/slices/confirmationMetrics/index.test.ts
+++ b/app/core/redux/slices/confirmationMetrics/index.test.ts
@@ -3,6 +3,7 @@ import reducer, {
   updateConfirmationMetric,
   selectConfirmationMetrics,
   ConfirmationMetrics,
+  selectConfirmationMetricsById,
 } from './index';
 import { RootState } from '../../../../reducers';
 
@@ -91,5 +92,24 @@ describe('confirmationMetrics slice', () => {
 
       expect(selectConfirmationMetrics(state)).toEqual(metricsById);
     });
+
+    it('selects confirmation metrics by ID', () => {
+      const metricsById = {
+        'id-1': {
+          properties: { prop1: 'value1' },
+          sensitiveProperties: { sensitive: 'secret' },
+        },
+        'id-2': {
+          properties: { prop2: 'value2' },
+          sensitiveProperties: { sensitive2: 'secret2' },
+        },
+      };
+
+      const state = {
+        confirmationMetrics: { metricsById },
+      } as unknown as RootState;
+
+      expect(selectConfirmationMetricsById(state, 'id-1')).toEqual(metricsById['id-1']);
+    });
   });
 });
diff --git a/app/core/redux/slices/confirmationMetrics/index.ts b/app/core/redux/slices/confirmationMetrics/index.ts
index 4e1e61868f3..b4f743573bc 100644
--- a/app/core/redux/slices/confirmationMetrics/index.ts
+++ b/app/core/redux/slices/confirmationMetrics/index.ts
@@ -1,6 +1,7 @@
 import { merge } from 'lodash';
 import { PayloadAction, createSlice } from '@reduxjs/toolkit';
 import { RootState } from '../../../../reducers';
+import { createSelector } from 'reselect';
 
 export interface ConfirmationMetrics {
   properties?: Record<string, unknown>;
@@ -50,3 +51,8 @@ export const { updateConfirmationMetric } = actions;
 // Selectors
 export const selectConfirmationMetrics = (state: RootState) =>
   state[name].metricsById;
+
+export const selectConfirmationMetricsById = createSelector(
+  [selectConfirmationMetrics, (_: RootState, id: string) => id],
+  (metricsById, id) => metricsById[id]
+);


Reviews:
user: login: OGPoyraz
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-28T11:57:33Z
user: login: vinistevam
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-28T12:10:24Z
user: login: OGPoyraz
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-28T12:17:27Z
user: login: vinistevam
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-31T10:48:03Z
user: login: OGPoyraz
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-31T12:20:58Z
user: login: OGPoyraz
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-31T12:23:32Z
user: login: OGPoyraz
state: DISMISSED
author_association: MEMBER
submitted_at: 2025-03-31T12:23:57Z
user: login: vinistevam
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-01T07:52:49Z
user: login: OGPoyraz
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-02T06:39:52Z
user: login: OGPoyraz
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-03T12:35:09Z
user: login: OGPoyraz
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-03T12:35:17Z
user: login: OGPoyraz
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-03T12:35:27Z
user: login: OGPoyraz
state: APPROVED
author_association: MEMBER
submitted_at: 2025-04-03T12:35:38Z
user: login: matthewwalsh0
state: APPROVED
author_association: MEMBER
submitted_at: 2025-04-04T09:50:34Z

Review comments:
diff_hunk: @@ -0,0 +1,104 @@
+import { useMemo, useEffect } from 'react';
+import { useSelector } from 'react-redux';
+import {
+  selectConfirmationMetricsById,
+} from '../../../../core/redux/slices/confirmationMetrics';
+import { useAlerts } from '../AlertSystem/context';
+import { useConfirmationMetricEvents } from './useConfirmationMetricEvents';
+import { Alert } from '../types/alerts';
+import { RootState } from '../../../../reducers';
+import { useSignatureRequest } from './useSignatureRequest';
+import { AlertKeys } from '../constants/alerts';
+
+type AlertNameMetrics = {
+  [K in AlertKeys]: string;
+};
+
+export function useConfirmationAlertMetric() {
+  const { setConfirmationMetric } = useConfirmationMetricEvents();
+  const { alerts, isAlertConfirmed, alertKey } = useAlerts();
+  const signatureRequest = useSignatureRequest();
+
+  const alertProperties = useMemo(() => ({
+    alert_trigger_count: alerts.length,
+    alert_trigger_name: getAlertNames(alerts),
+    alert_resolved_count: alerts.filter((alertSelected) =>
+      isAlertConfirmed(alertSelected.key),
+    ).length,
+    alert_resolved: getAlertNames(
+      alerts.filter((alertSelected) => isAlertConfirmed(alertSelected.key)),
+    ),
+  }), [alerts, isAlertConfirmed]);
+
+  const confirmationMetrics = useSelector((state: RootState) =>
+    selectConfirmationMetricsById(state, signatureRequest?.id ?? '')
+  );
+
+  useEffect(() => {
+    setConfirmationMetric({
+      properties: {
+        ...alertProperties,
+      },
+    });
+  }, [alertProperties, setConfirmationMetric]);
path: app/components/Views/confirmations/hooks/useConfirmationAlertMetric.ts
user: login: OGPoyraz
body: My only concern about this is, `useConfirmationAlertMetric` hook is called in both `AlertRow` and `AlertModal`.
For example if we have 2 `AlertRow` and 2 `AlertModal` in a confirmation, any update will trigger this hook 4 times and 3 of the updates will be unnecessary. 

I was wondering if there is a way to deal with that.
created_at: 2025-03-28T11:57:32Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,104 @@
+import { useMemo, useEffect } from 'react';
+import { useSelector } from 'react-redux';
+import {
+  selectConfirmationMetricsById,
+} from '../../../../core/redux/slices/confirmationMetrics';
+import { useAlerts } from '../AlertSystem/context';
+import { useConfirmationMetricEvents } from './useConfirmationMetricEvents';
+import { Alert } from '../types/alerts';
+import { RootState } from '../../../../reducers';
+import { useSignatureRequest } from './useSignatureRequest';
+import { AlertKeys } from '../constants/alerts';
+
+type AlertNameMetrics = {
+  [K in AlertKeys]: string;
+};
+
+export function useConfirmationAlertMetric() {
+  const { setConfirmationMetric } = useConfirmationMetricEvents();
+  const { alerts, isAlertConfirmed, alertKey } = useAlerts();
+  const signatureRequest = useSignatureRequest();
+
+  const alertProperties = useMemo(() => ({
+    alert_trigger_count: alerts.length,
+    alert_trigger_name: getAlertNames(alerts),
+    alert_resolved_count: alerts.filter((alertSelected) =>
+      isAlertConfirmed(alertSelected.key),
+    ).length,
+    alert_resolved: getAlertNames(
+      alerts.filter((alertSelected) => isAlertConfirmed(alertSelected.key)),
+    ),
+  }), [alerts, isAlertConfirmed]);
+
+  const confirmationMetrics = useSelector((state: RootState) =>
+    selectConfirmationMetricsById(state, signatureRequest?.id ?? '')
+  );
+
+  useEffect(() => {
+    setConfirmationMetric({
+      properties: {
+        ...alertProperties,
+      },
+    });
+  }, [alertProperties, setConfirmationMetric]);
path: app/components/Views/confirmations/hooks/useConfirmationAlertMetric.ts
user: login: vinistevam
body: I see your point.
In `AlertRow`, the update is triggered only when the user clicks on the specific alert, which opens the `AlertModal`, and it updates the metrics for that alert calling `trackAlertRendered`. If there are multiple alerts and the user navigates to another one, the `trackAlertRendered` function will be triggered again, ensuring the metrics are updated for each displayed alert.
created_at: 2025-03-28T12:10:24Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,104 @@
+import { useMemo, useEffect } from 'react';
+import { useSelector } from 'react-redux';
+import {
+  selectConfirmationMetricsById,
+} from '../../../../core/redux/slices/confirmationMetrics';
+import { useAlerts } from '../AlertSystem/context';
+import { useConfirmationMetricEvents } from './useConfirmationMetricEvents';
+import { Alert } from '../types/alerts';
+import { RootState } from '../../../../reducers';
+import { useSignatureRequest } from './useSignatureRequest';
+import { AlertKeys } from '../constants/alerts';
+
+type AlertNameMetrics = {
+  [K in AlertKeys]: string;
+};
+
+export function useConfirmationAlertMetric() {
+  const { setConfirmationMetric } = useConfirmationMetricEvents();
+  const { alerts, isAlertConfirmed, alertKey } = useAlerts();
+  const signatureRequest = useSignatureRequest();
+
+  const alertProperties = useMemo(() => ({
+    alert_trigger_count: alerts.length,
+    alert_trigger_name: getAlertNames(alerts),
+    alert_resolved_count: alerts.filter((alertSelected) =>
+      isAlertConfirmed(alertSelected.key),
+    ).length,
+    alert_resolved: getAlertNames(
+      alerts.filter((alertSelected) => isAlertConfirmed(alertSelected.key)),
+    ),
+  }), [alerts, isAlertConfirmed]);
+
+  const confirmationMetrics = useSelector((state: RootState) =>
+    selectConfirmationMetricsById(state, signatureRequest?.id ?? '')
+  );
+
+  useEffect(() => {
+    setConfirmationMetric({
+      properties: {
+        ...alertProperties,
+      },
+    });
+  }, [alertProperties, setConfirmationMetric]);
path: app/components/Views/confirmations/hooks/useConfirmationAlertMetric.ts
user: login: OGPoyraz
body: Oh I see, so they all update different parts of state. Which will not trigger the hook because `alertProperties` are not changing, that's right.

But it still doesn't change the fact that initially will be called 4 times if this hook is used 4 different instances right? 
created_at: 2025-03-28T12:17:27Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,104 @@
+import { useMemo, useEffect } from 'react';
+import { useSelector } from 'react-redux';
+import {
+  selectConfirmationMetricsById,
+} from '../../../../core/redux/slices/confirmationMetrics';
+import { useAlerts } from '../AlertSystem/context';
+import { useConfirmationMetricEvents } from './useConfirmationMetricEvents';
+import { Alert } from '../types/alerts';
+import { RootState } from '../../../../reducers';
+import { useSignatureRequest } from './useSignatureRequest';
+import { AlertKeys } from '../constants/alerts';
+
+type AlertNameMetrics = {
+  [K in AlertKeys]: string;
+};
+
+export function useConfirmationAlertMetric() {
+  const { setConfirmationMetric } = useConfirmationMetricEvents();
+  const { alerts, isAlertConfirmed, alertKey } = useAlerts();
+  const signatureRequest = useSignatureRequest();
+
+  const alertProperties = useMemo(() => ({
+    alert_trigger_count: alerts.length,
+    alert_trigger_name: getAlertNames(alerts),
+    alert_resolved_count: alerts.filter((alertSelected) =>
+      isAlertConfirmed(alertSelected.key),
+    ).length,
+    alert_resolved: getAlertNames(
+      alerts.filter((alertSelected) => isAlertConfirmed(alertSelected.key)),
+    ),
+  }), [alerts, isAlertConfirmed]);
+
+  const confirmationMetrics = useSelector((state: RootState) =>
+    selectConfirmationMetricsById(state, signatureRequest?.id ?? '')
+  );
+
+  useEffect(() => {
+    setConfirmationMetric({
+      properties: {
+        ...alertProperties,
+      },
+    });
+  }, [alertProperties, setConfirmationMetric]);
path: app/components/Views/confirmations/hooks/useConfirmationAlertMetric.ts
user: login: vinistevam
body: You are right, I moved the `useConfirmationAlertMetrics` to a `ConfirmAlerts` component to self-contained everything around alerts and pass the functions from metrics via context as we have in the extension.
created_at: 2025-03-31T10:48:03Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,104 @@
+import { useMemo, useEffect } from 'react';
+import { useSelector } from 'react-redux';
+import {
+  selectConfirmationMetricsById,
+} from '../../../../core/redux/slices/confirmationMetrics';
+import { useAlerts } from '../AlertSystem/context';
+import { useConfirmationMetricEvents } from './useConfirmationMetricEvents';
+import { Alert } from '../types/alerts';
+import { RootState } from '../../../../reducers';
+import { useSignatureRequest } from './useSignatureRequest';
+import { AlertKeys } from '../constants/alerts';
+
+type AlertNameMetrics = {
+  [K in AlertKeys]: string;
+};
+
+export function useConfirmationAlertMetric() {
+  const { setConfirmationMetric } = useConfirmationMetricEvents();
+  const { alerts, isAlertConfirmed, alertKey } = useAlerts();
+  const signatureRequest = useSignatureRequest();
+
+  const alertProperties = useMemo(() => ({
+    alert_trigger_count: alerts.length,
+    alert_trigger_name: getAlertNames(alerts),
+    alert_resolved_count: alerts.filter((alertSelected) =>
+      isAlertConfirmed(alertSelected.key),
+    ).length,
+    alert_resolved: getAlertNames(
+      alerts.filter((alertSelected) => isAlertConfirmed(alertSelected.key)),
+    ),
+  }), [alerts, isAlertConfirmed]);
+
+  const confirmationMetrics = useSelector((state: RootState) =>
+    selectConfirmationMetricsById(state, signatureRequest?.id ?? '')
+  );
+
+  useEffect(() => {
+    setConfirmationMetric({
+      properties: {
+        ...alertProperties,
+      },
+    });
+  }, [alertProperties, setConfirmationMetric]);
path: app/components/Views/confirmations/hooks/useConfirmationAlertMetric.ts
user: login: OGPoyraz
body: That make sense, so now context is providing those callback. 
We could've just call the update in context too - but at this point it's just preference. 
created_at: 2025-03-31T12:20:57Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,109 @@
+import { useMemo, useEffect, useCallback } from 'react';
+import { useSelector } from 'react-redux';
+import {
+  selectConfirmationMetricsById,
+} from '../../../../core/redux/slices/confirmationMetrics';
+import { useAlerts } from '../AlertSystem/context';
+import { useConfirmationMetricEvents } from './useConfirmationMetricEvents';
+import { Alert } from '../types/alerts';
+import { RootState } from '../../../../reducers';
+import { useSignatureRequest } from './useSignatureRequest';
+import { AlertKeys } from '../constants/alerts';
+
+type AlertNameMetrics = {
+  [K in AlertKeys]: string;
+};
+
+export function useConfirmationAlertMetrics() {
+  const { setConfirmationMetric } = useConfirmationMetricEvents();
+  const { alerts, isAlertConfirmed, alertKey } = useAlerts();
+  const signatureRequest = useSignatureRequest();
+
+  const alertProperties = useMemo(() => alerts.length > 0 ? {
+    alert_trigger_count: alerts.length,
+    alert_trigger_name: getAlertNames(alerts),
+    alert_resolved_count: alerts.filter((alertSelected) =>
+      isAlertConfirmed(alertSelected.key),
+    ).length,
+    alert_resolved: getAlertNames(
+      alerts.filter((alertSelected) => isAlertConfirmed(alertSelected.key)),
+    ),
+  } : undefined, [alerts, isAlertConfirmed]);
+
+  const confirmationMetrics = useSelector((state: RootState) =>
+    selectConfirmationMetricsById(state, signatureRequest?.id ?? '')
+  );
+
+    const trackInlineAlertClicked = useCallback((alertField?: string) => {
+      const alertKeyClicked = uniqueFreshArrayPush(
+        (confirmationMetrics?.properties?.alert_key_clicked as string[]) ?? [],
+        getAlertName(alertKey)
+      );
+      const alertFieldClickedMetrics = confirmationMetrics?.properties?.alert_field_clicked as string[] ?? [];
+      const alertFieldClicked = alertField ? uniqueFreshArrayPush(
+        alertFieldClickedMetrics,
+        alertField) : alertFieldClickedMetrics;
+
+      setConfirmationMetric({
+        properties: {
+          ...alertProperties,
+          alert_key_clicked: alertKeyClicked,
+          alert_field_clicked: alertFieldClicked,
+        },
+      });
+    }, [confirmationMetrics, alertKey, setConfirmationMetric, alertProperties]);
+
+    const trackAlertRendered = useCallback(() => {
+      const alertVisualized = uniqueFreshArrayPush(
+        (confirmationMetrics?.properties?.alert_visualized as string[]) ?? [],
+        getAlertName(alertKey)
+      );
+
+      setConfirmationMetric({
+        properties: {
+          ...alertProperties,
+          alert_visualized: alertVisualized,
+          alert_visualized_count: alertVisualized.length,
+        },
+      });
+  }, [confirmationMetrics, alertKey, setConfirmationMetric, alertProperties]);
+
+    const updateAlertMetrics = useCallback(() => {
+      if (!alertProperties) {
+        return;
+      }
+      setConfirmationMetric({
+        properties: {
+          ...alertProperties,
+        },
+      });
+
+    }, [alertProperties, setConfirmationMetric]);
+
+    useEffect(() => {
+      updateAlertMetrics();
+    }, [updateAlertMetrics]);
+
+  return {
+    trackAlertRendered,
+    trackInlineAlertClicked,
+    updateAlertMetrics,
path: app/components/Views/confirmations/hooks/useConfirmationAlertMetrics.ts
user: login: OGPoyraz
body: Shall we remove the `updateAlertMetrics` here if this hook is responsible to do updates now? 
created_at: 2025-03-31T12:23:32Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,109 @@
+import { useMemo, useEffect, useCallback } from 'react';
+import { useSelector } from 'react-redux';
+import {
+  selectConfirmationMetricsById,
+} from '../../../../core/redux/slices/confirmationMetrics';
+import { useAlerts } from '../AlertSystem/context';
+import { useConfirmationMetricEvents } from './useConfirmationMetricEvents';
+import { Alert } from '../types/alerts';
+import { RootState } from '../../../../reducers';
+import { useSignatureRequest } from './useSignatureRequest';
+import { AlertKeys } from '../constants/alerts';
+
+type AlertNameMetrics = {
+  [K in AlertKeys]: string;
+};
+
+export function useConfirmationAlertMetrics() {
+  const { setConfirmationMetric } = useConfirmationMetricEvents();
+  const { alerts, isAlertConfirmed, alertKey } = useAlerts();
+  const signatureRequest = useSignatureRequest();
+
+  const alertProperties = useMemo(() => alerts.length > 0 ? {
+    alert_trigger_count: alerts.length,
+    alert_trigger_name: getAlertNames(alerts),
+    alert_resolved_count: alerts.filter((alertSelected) =>
+      isAlertConfirmed(alertSelected.key),
+    ).length,
+    alert_resolved: getAlertNames(
+      alerts.filter((alertSelected) => isAlertConfirmed(alertSelected.key)),
+    ),
+  } : undefined, [alerts, isAlertConfirmed]);
+
+  const confirmationMetrics = useSelector((state: RootState) =>
+    selectConfirmationMetricsById(state, signatureRequest?.id ?? '')
+  );
+
+    const trackInlineAlertClicked = useCallback((alertField?: string) => {
+      const alertKeyClicked = uniqueFreshArrayPush(
+        (confirmationMetrics?.properties?.alert_key_clicked as string[]) ?? [],
+        getAlertName(alertKey)
+      );
+      const alertFieldClickedMetrics = confirmationMetrics?.properties?.alert_field_clicked as string[] ?? [];
+      const alertFieldClicked = alertField ? uniqueFreshArrayPush(
+        alertFieldClickedMetrics,
+        alertField) : alertFieldClickedMetrics;
+
+      setConfirmationMetric({
+        properties: {
+          ...alertProperties,
+          alert_key_clicked: alertKeyClicked,
+          alert_field_clicked: alertFieldClicked,
+        },
+      });
+    }, [confirmationMetrics, alertKey, setConfirmationMetric, alertProperties]);
+
+    const trackAlertRendered = useCallback(() => {
+      const alertVisualized = uniqueFreshArrayPush(
+        (confirmationMetrics?.properties?.alert_visualized as string[]) ?? [],
+        getAlertName(alertKey)
+      );
+
+      setConfirmationMetric({
+        properties: {
+          ...alertProperties,
+          alert_visualized: alertVisualized,
+          alert_visualized_count: alertVisualized.length,
+        },
+      });
+  }, [confirmationMetrics, alertKey, setConfirmationMetric, alertProperties]);
+
+    const updateAlertMetrics = useCallback(() => {
+      if (!alertProperties) {
+        return;
+      }
+      setConfirmationMetric({
+        properties: {
+          ...alertProperties,
+        },
+      });
+
+    }, [alertProperties, setConfirmationMetric]);
+
+    useEffect(() => {
+      updateAlertMetrics();
+    }, [updateAlertMetrics]);
+
+  return {
+    trackAlertRendered,
+    trackInlineAlertClicked,
+    updateAlertMetrics,
path: app/components/Views/confirmations/hooks/useConfirmationAlertMetrics.ts
user: login: vinistevam
body: Actually, I've been thinking about what you said in make this hook similar to `useConfirmationMetricEvents` and I've applied those changes, which diminished 1/3 of the changes. So when you have time, could you review it again? :pray:  
created_at: 2025-04-01T07:52:49Z
author_association: CONTRIBUTOR
diff_hunk: @@ -68,6 +70,10 @@ export const Footer = () => {
     await onConfirm();
   }, [hasDangerAlerts, onConfirm, showConfirmAlertModal]);
 
+  useEffect(() => {
+    trackAlertMetrics();
+  }, [alerts, trackAlertMetrics]);
+
path: app/components/Views/confirmations/components/Confirm/Footer/Footer.tsx
user: login: OGPoyraz
body: I was wondering why we didn't do this update tracking in `useConfirmationAlerts` hook. Since that hook is collecting all alert data - wouldn't it be perfect place to call updater? It's used once and it has all `alerts` data already - it could also work as updater 
created_at: 2025-04-02T06:35:58Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,105 @@
+import { useMemo, useCallback } from 'react';
+import { useSelector } from 'react-redux';
+import {
+  selectConfirmationMetricsById,
+} from '../../../../core/redux/slices/confirmationMetrics';
+import { useAlerts } from '../AlertSystem/context';
+import { useConfirmationMetricEvents } from './useConfirmationMetricEvents';
+import { Alert } from '../types/alerts';
+import { RootState } from '../../../../reducers';
+import { useSignatureRequest } from './useSignatureRequest';
+import { AlertKeys } from '../constants/alerts';
+
+type AlertNameMetrics = {
+  [K in AlertKeys]: string;
+};
+
+export function useConfirmationAlertMetrics() {
+  const { setConfirmationMetric } = useConfirmationMetricEvents();
+  const { alerts, isAlertConfirmed, alertKey } = useAlerts();
+  const signatureRequest = useSignatureRequest();
+
+  const alertProperties = useMemo(() => alerts.length > 0 ? {
+    alert_trigger_count: alerts.length,
+    alert_trigger_name: getAlertNames(alerts),
+    alert_resolved_count: alerts.filter((alertSelected) =>
+      isAlertConfirmed(alertSelected.key),
+    ).length,
+    alert_resolved: getAlertNames(
+      alerts.filter((alertSelected) => isAlertConfirmed(alertSelected.key)),
+    ),
+  } : undefined, [alerts, isAlertConfirmed]);
path: app/components/Views/confirmations/hooks/useConfirmationAlertMetrics.ts
user: login: OGPoyraz
body: If we do updating in `useConfirmationAlerts` this could be carried over there
created_at: 2025-04-02T06:36:31Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,105 @@
+import { useMemo, useCallback } from 'react';
+import { useSelector } from 'react-redux';
+import {
+  selectConfirmationMetricsById,
+} from '../../../../core/redux/slices/confirmationMetrics';
+import { useAlerts } from '../AlertSystem/context';
+import { useConfirmationMetricEvents } from './useConfirmationMetricEvents';
+import { Alert } from '../types/alerts';
+import { RootState } from '../../../../reducers';
+import { useSignatureRequest } from './useSignatureRequest';
+import { AlertKeys } from '../constants/alerts';
+
+type AlertNameMetrics = {
+  [K in AlertKeys]: string;
+};
+
+export function useConfirmationAlertMetrics() {
+  const { setConfirmationMetric } = useConfirmationMetricEvents();
+  const { alerts, isAlertConfirmed, alertKey } = useAlerts();
+  const signatureRequest = useSignatureRequest();
+
+  const alertProperties = useMemo(() => alerts.length > 0 ? {
+    alert_trigger_count: alerts.length,
+    alert_trigger_name: getAlertNames(alerts),
+    alert_resolved_count: alerts.filter((alertSelected) =>
+      isAlertConfirmed(alertSelected.key),
+    ).length,
+    alert_resolved: getAlertNames(
+      alerts.filter((alertSelected) => isAlertConfirmed(alertSelected.key)),
+    ),
+  } : undefined, [alerts, isAlertConfirmed]);
+
+  const confirmationMetrics = useSelector((state: RootState) =>
+    selectConfirmationMetricsById(state, signatureRequest?.id ?? '')
+  );
+
+    const trackInlineAlertClicked = useCallback((alertField?: string) => {
+      const alertKeyClicked = uniqueFreshArrayPush(
+        (confirmationMetrics?.properties?.alert_key_clicked as string[]) ?? [],
+        getAlertName(alertKey)
+      );
+      const alertFieldClickedMetrics = confirmationMetrics?.properties?.alert_field_clicked as string[] ?? [];
+      const alertFieldClicked = alertField ? uniqueFreshArrayPush(
+        alertFieldClickedMetrics,
+        alertField) : alertFieldClickedMetrics;
+
+      setConfirmationMetric({
+        properties: {
+          ...alertProperties,
path: app/components/Views/confirmations/hooks/useConfirmationAlertMetrics.ts
user: login: OGPoyraz
body: Since `setConfirmationMetric` is doing an additive updates - this function (`trackInlineAlertClicked`) doesn't need to also apply `alertProperties`. If we assume `useConfirmationAlerts` is already updating `alertProperties` on it's own, this function could just update `alert_key_clicked` and `alert_field_clicked`
created_at: 2025-04-02T06:38:18Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,105 @@
+import { useMemo, useCallback } from 'react';
+import { useSelector } from 'react-redux';
+import {
+  selectConfirmationMetricsById,
+} from '../../../../core/redux/slices/confirmationMetrics';
+import { useAlerts } from '../AlertSystem/context';
+import { useConfirmationMetricEvents } from './useConfirmationMetricEvents';
+import { Alert } from '../types/alerts';
+import { RootState } from '../../../../reducers';
+import { useSignatureRequest } from './useSignatureRequest';
+import { AlertKeys } from '../constants/alerts';
+
+type AlertNameMetrics = {
+  [K in AlertKeys]: string;
+};
+
+export function useConfirmationAlertMetrics() {
+  const { setConfirmationMetric } = useConfirmationMetricEvents();
+  const { alerts, isAlertConfirmed, alertKey } = useAlerts();
+  const signatureRequest = useSignatureRequest();
+
+  const alertProperties = useMemo(() => alerts.length > 0 ? {
+    alert_trigger_count: alerts.length,
+    alert_trigger_name: getAlertNames(alerts),
+    alert_resolved_count: alerts.filter((alertSelected) =>
+      isAlertConfirmed(alertSelected.key),
+    ).length,
+    alert_resolved: getAlertNames(
+      alerts.filter((alertSelected) => isAlertConfirmed(alertSelected.key)),
+    ),
+  } : undefined, [alerts, isAlertConfirmed]);
+
+  const confirmationMetrics = useSelector((state: RootState) =>
+    selectConfirmationMetricsById(state, signatureRequest?.id ?? '')
+  );
+
+    const trackInlineAlertClicked = useCallback((alertField?: string) => {
+      const alertKeyClicked = uniqueFreshArrayPush(
+        (confirmationMetrics?.properties?.alert_key_clicked as string[]) ?? [],
+        getAlertName(alertKey)
+      );
+      const alertFieldClickedMetrics = confirmationMetrics?.properties?.alert_field_clicked as string[] ?? [];
+      const alertFieldClicked = alertField ? uniqueFreshArrayPush(
+        alertFieldClickedMetrics,
+        alertField) : alertFieldClickedMetrics;
+
+      setConfirmationMetric({
+        properties: {
+          ...alertProperties,
+          alert_key_clicked: alertKeyClicked,
+          alert_field_clicked: alertFieldClicked,
+        },
+      });
+    }, [confirmationMetrics, alertKey, setConfirmationMetric, alertProperties]);
+
+    const trackAlertRendered = useCallback(() => {
+      const alertVisualized = uniqueFreshArrayPush(
+        (confirmationMetrics?.properties?.alert_visualized as string[]) ?? [],
+        getAlertName(alertKey)
+      );
+
+      setConfirmationMetric({
+        properties: {
+          ...alertProperties,
path: app/components/Views/confirmations/hooks/useConfirmationAlertMetrics.ts
user: login: OGPoyraz
body: Same comment above 
created_at: 2025-04-02T06:38:52Z
author_association: MEMBER
diff_hunk: @@ -68,6 +70,10 @@ export const Footer = () => {
     await onConfirm();
   }, [hasDangerAlerts, onConfirm, showConfirmAlertModal]);
 
+  useEffect(() => {
+    trackAlertMetrics();
+  }, [alerts, trackAlertMetrics]);
+
path: app/components/Views/confirmations/components/Confirm/Footer/Footer.tsx
user: login: OGPoyraz
body: Resolving these after 1on1 discussion
created_at: 2025-04-03T12:35:09Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,105 @@
+import { useMemo, useCallback } from 'react';
+import { useSelector } from 'react-redux';
+import {
+  selectConfirmationMetricsById,
+} from '../../../../core/redux/slices/confirmationMetrics';
+import { useAlerts } from '../AlertSystem/context';
+import { useConfirmationMetricEvents } from './useConfirmationMetricEvents';
+import { Alert } from '../types/alerts';
+import { RootState } from '../../../../reducers';
+import { useSignatureRequest } from './useSignatureRequest';
+import { AlertKeys } from '../constants/alerts';
+
+type AlertNameMetrics = {
+  [K in AlertKeys]: string;
+};
+
+export function useConfirmationAlertMetrics() {
+  const { setConfirmationMetric } = useConfirmationMetricEvents();
+  const { alerts, isAlertConfirmed, alertKey } = useAlerts();
+  const signatureRequest = useSignatureRequest();
+
+  const alertProperties = useMemo(() => alerts.length > 0 ? {
+    alert_trigger_count: alerts.length,
+    alert_trigger_name: getAlertNames(alerts),
+    alert_resolved_count: alerts.filter((alertSelected) =>
+      isAlertConfirmed(alertSelected.key),
+    ).length,
+    alert_resolved: getAlertNames(
+      alerts.filter((alertSelected) => isAlertConfirmed(alertSelected.key)),
+    ),
+  } : undefined, [alerts, isAlertConfirmed]);
path: app/components/Views/confirmations/hooks/useConfirmationAlertMetrics.ts
user: login: OGPoyraz
body: Resolving these after 1on1 discussion
created_at: 2025-04-03T12:35:17Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,105 @@
+import { useMemo, useCallback } from 'react';
+import { useSelector } from 'react-redux';
+import {
+  selectConfirmationMetricsById,
+} from '../../../../core/redux/slices/confirmationMetrics';
+import { useAlerts } from '../AlertSystem/context';
+import { useConfirmationMetricEvents } from './useConfirmationMetricEvents';
+import { Alert } from '../types/alerts';
+import { RootState } from '../../../../reducers';
+import { useSignatureRequest } from './useSignatureRequest';
+import { AlertKeys } from '../constants/alerts';
+
+type AlertNameMetrics = {
+  [K in AlertKeys]: string;
+};
+
+export function useConfirmationAlertMetrics() {
+  const { setConfirmationMetric } = useConfirmationMetricEvents();
+  const { alerts, isAlertConfirmed, alertKey } = useAlerts();
+  const signatureRequest = useSignatureRequest();
+
+  const alertProperties = useMemo(() => alerts.length > 0 ? {
+    alert_trigger_count: alerts.length,
+    alert_trigger_name: getAlertNames(alerts),
+    alert_resolved_count: alerts.filter((alertSelected) =>
+      isAlertConfirmed(alertSelected.key),
+    ).length,
+    alert_resolved: getAlertNames(
+      alerts.filter((alertSelected) => isAlertConfirmed(alertSelected.key)),
+    ),
+  } : undefined, [alerts, isAlertConfirmed]);
+
+  const confirmationMetrics = useSelector((state: RootState) =>
+    selectConfirmationMetricsById(state, signatureRequest?.id ?? '')
+  );
+
+    const trackInlineAlertClicked = useCallback((alertField?: string) => {
+      const alertKeyClicked = uniqueFreshArrayPush(
+        (confirmationMetrics?.properties?.alert_key_clicked as string[]) ?? [],
+        getAlertName(alertKey)
+      );
+      const alertFieldClickedMetrics = confirmationMetrics?.properties?.alert_field_clicked as string[] ?? [];
+      const alertFieldClicked = alertField ? uniqueFreshArrayPush(
+        alertFieldClickedMetrics,
+        alertField) : alertFieldClickedMetrics;
+
+      setConfirmationMetric({
+        properties: {
+          ...alertProperties,
path: app/components/Views/confirmations/hooks/useConfirmationAlertMetrics.ts
user: login: OGPoyraz
body: Resolving these after 1on1 discussion
created_at: 2025-04-03T12:35:27Z
author_association: MEMBER

PR comments:
user: login: github-actions[bot]
created_at: 2025-03-26T11:23:47Z
body: **CLA Signature Action:** All authors have signed the CLA. You may need to manually re-run the blocking PR check if it doesn't pass in a few minutes.
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-03-26T11:32:51Z
body: ## [<img alt="https://bitrise.io/" src="https://assets-global.website-files.com/5db35de024bb983af1b4e151/5e6f9ccc3e129dfd8a205e4e_Bitrise%20Logo%20-%20Eggplant%20Bg.png" height="20">](https://app.bitrise.io/app/be69d4368ee7e86d/pipelines/850eebbb-d002-4917-a365-b8b85dd2c943) **Bitrise**

❌❌❌ `pr_smoke_e2e_pipeline` failed on Bitrise! ❌❌❌

Commit hash: 0761d6a4683174e6f86b2b69ddc5a2bb94bf0e33
Build link: https://app.bitrise.io/app/be69d4368ee7e86d/pipelines/850eebbb-d002-4917-a365-b8b85dd2c943

>[!NOTE]
>- You can kick off another `pr_smoke_e2e_pipeline` on Bitrise by removing and re-applying the `Run Smoke E2E` label on the pull request

> [!TIP]
>- Check the [documentation](https://www.notion.so/metamask-consensys/Bitrise-Pipeline-Overview-43159500c43748a389556f0593e8834b#26052f2ea6e24f8c9cfdb57a7522dc1f) if you have any doubts on how to understand the failure on bitrise

<!-- BITRISE_TAG -->
<!-- BITRISE_FAIL_TAG -->

<!-- 0761d6a4683174e6f86b2b69ddc5a2bb94bf0e33 -->
author_association: CONTRIBUTOR
user: login: codecov-commenter
created_at: 2025-03-27T10:26:20Z
body: ## [Codecov](https://app.codecov.io/gh/MetaMask/metamask-mobile/pull/14246?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=MetaMask) Report
Attention: Patch coverage is `93.47826%` with `3 lines` in your changes missing coverage. Please review.
> Please [upload](https://docs.codecov.com/docs/codecov-uploader) report for BASE (`test/add-e2e-alert-system@57e240e`). [Learn more](https://docs.codecov.io/docs/error-reference?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=MetaMask#section-missing-base-commit) about missing BASE report.

| [Files with missing lines](https://app.codecov.io/gh/MetaMask/metamask-mobile/pull/14246?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=MetaMask) | Patch % | Lines |
|---|---|---|
| [.../confirmations/hooks/useConfirmationAlertMetric.ts](https://app.codecov.io/gh/MetaMask/metamask-mobile/pull/14246?src=pr&el=tree&filepath=app%2Fcomponents%2FViews%2Fconfirmations%2Fhooks%2FuseConfirmationAlertMetric.ts&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=MetaMask#diff-YXBwL2NvbXBvbmVudHMvVmlld3MvY29uZmlybWF0aW9ucy9ob29rcy91c2VDb25maXJtYXRpb25BbGVydE1ldHJpYy50cw==) | 93.10% | [0 Missing and 2 partials :warning: ](https://app.codecov.io/gh/MetaMask/metamask-mobile/pull/14246?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=MetaMask) |
| [...ations/components/UI/InfoRow/AlertRow/AlertRow.tsx](https://app.codecov.io/gh/MetaMask/metamask-mobile/pull/14246?src=pr&el=tree&filepath=app%2Fcomponents%2FViews%2Fconfirmations%2Fcomponents%2FUI%2FInfoRow%2FAlertRow%2FAlertRow.tsx&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=MetaMask#diff-YXBwL2NvbXBvbmVudHMvVmlld3MvY29uZmlybWF0aW9ucy9jb21wb25lbnRzL1VJL0luZm9Sb3cvQWxlcnRSb3cvQWxlcnRSb3cudHN4) | 85.71% | [0 Missing and 1 partial :warning: ](https://app.codecov.io/gh/MetaMask/metamask-mobile/pull/14246?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=MetaMask) |

<details><summary>Additional details and impacted files</summary>


```diff
@@                     Coverage Diff                      @@
##             test/add-e2e-alert-system   #14246   +/-   ##
============================================================
  Coverage                             ?   65.70%           
============================================================
  Files                                ?     2227           
  Lines                                ?    47454           
  Branches                             ?     6681           
============================================================
  Hits                                 ?    31181           
  Misses                               ?    14277           
  Partials                             ?     1996           
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/MetaMask/metamask-mobile/pull/14246?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=MetaMask).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=MetaMask).

<details><summary> :rocket: New features to boost your workflow: </summary>

- :snowflake: [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
- :package: [JS Bundle Analysis](https://docs.codecov.com/docs/javascript-bundle-analysis): Save yourself from yourself by tracking and limiting bundle sizes in JS merges.
</details>
author_association: NONE
user: login: github-actions[bot]
created_at: 2025-03-28T08:31:01Z
body: ## [<img alt="https://bitrise.io/" src="https://assets-global.website-files.com/5db35de024bb983af1b4e151/5e6f9ccc3e129dfd8a205e4e_Bitrise%20Logo%20-%20Eggplant%20Bg.png" height="20">](https://app.bitrise.io/app/be69d4368ee7e86d/pipelines/8d457785-9ba9-44b0-b67e-340678221748) **Bitrise**

✅✅✅ `pr_smoke_e2e_pipeline` passed on Bitrise! ✅✅✅

Commit hash: 08a15d492b24aefbf68478d0397171c3205a3c4b
Build link: https://app.bitrise.io/app/be69d4368ee7e86d/pipelines/8d457785-9ba9-44b0-b67e-340678221748

>[!NOTE]
>- You can kick off another `pr_smoke_e2e_pipeline` on Bitrise by removing and re-applying the `Run Smoke E2E` label on the pull request

<!-- BITRISE_TAG -->
<!-- BITRISE_SUCCESS_TAG -->

<!-- 08a15d492b24aefbf68478d0397171c3205a3c4b -->
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-04-01T07:59:50Z
body: ## [<img alt="https://bitrise.io/" src="https://assets-global.website-files.com/5db35de024bb983af1b4e151/5e6f9ccc3e129dfd8a205e4e_Bitrise%20Logo%20-%20Eggplant%20Bg.png" height="20">](https://app.bitrise.io/app/be69d4368ee7e86d/pipelines/c45c3484-1683-4d5d-b120-cdfc8d09db49) **Bitrise**

✅✅✅ `pr_smoke_e2e_pipeline` passed on Bitrise! ✅✅✅

Commit hash: a8b4f14514e60cdbb5737f6ea3f67a21f259ed18
Build link: https://app.bitrise.io/app/be69d4368ee7e86d/pipelines/c45c3484-1683-4d5d-b120-cdfc8d09db49

>[!NOTE]
>- You can kick off another `pr_smoke_e2e_pipeline` on Bitrise by removing and re-applying the `Run Smoke E2E` label on the pull request

<!-- BITRISE_TAG -->
<!-- BITRISE_SUCCESS_TAG -->

<!-- a8b4f14514e60cdbb5737f6ea3f67a21f259ed18 -->
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-04-04T07:19:27Z
body: ## [<img alt="https://bitrise.io/" src="https://assets-global.website-files.com/5db35de024bb983af1b4e151/5e6f9ccc3e129dfd8a205e4e_Bitrise%20Logo%20-%20Eggplant%20Bg.png" height="20">](https://app.bitrise.io/app/be69d4368ee7e86d/pipelines/cb031a95-c854-4f4e-976a-ff6942139f49) **Bitrise**

❌❌❌ `pr_smoke_e2e_pipeline` failed on Bitrise! ❌❌❌

Commit hash: f6b61a7f77f9e6036d11103e7f120e86e2b23a35
Build link: https://app.bitrise.io/app/be69d4368ee7e86d/pipelines/cb031a95-c854-4f4e-976a-ff6942139f49

>[!NOTE]
>- You can kick off another `pr_smoke_e2e_pipeline` on Bitrise by removing and re-applying the `Run Smoke E2E` label on the pull request

> [!TIP]
>- Check the [documentation](https://www.notion.so/metamask-consensys/Bitrise-Pipeline-Overview-43159500c43748a389556f0593e8834b#26052f2ea6e24f8c9cfdb57a7522dc1f) if you have any doubts on how to understand the failure on bitrise

<!-- BITRISE_TAG -->
<!-- BITRISE_FAIL_TAG -->

<!-- f6b61a7f77f9e6036d11103e7f120e86e2b23a35 -->
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-04-07T04:51:38Z
body: ## [<img alt="https://bitrise.io/" src="https://assets-global.website-files.com/5db35de024bb983af1b4e151/5e6f9ccc3e129dfd8a205e4e_Bitrise%20Logo%20-%20Eggplant%20Bg.png" height="20">](https://app.bitrise.io/app/be69d4368ee7e86d/pipelines/d5808870-6f66-4ca9-9be4-661fa9f00361) **Bitrise**

✅✅✅ `pr_smoke_e2e_pipeline` passed on Bitrise! ✅✅✅

Commit hash: 51de536b5594d3c539bfd451e653d51c04adbee9
Build link: https://app.bitrise.io/app/be69d4368ee7e86d/pipelines/d5808870-6f66-4ca9-9be4-661fa9f00361

>[!NOTE]
>- You can kick off another `pr_smoke_e2e_pipeline` on Bitrise by removing and re-applying the `Run Smoke E2E` label on the pull request

<!-- BITRISE_TAG -->
<!-- BITRISE_SUCCESS_TAG -->

<!-- 51de536b5594d3c539bfd451e653d51c04adbee9 -->
author_association: CONTRIBUTOR
user: login: sonarqubecloud[bot]
created_at: 2025-04-07T05:03:14Z
body: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=metamask-mobile&pullRequest=14246) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 New issues](https://sonarcloud.io/project/issues?id=metamask-mobile&pullRequest=14246&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=metamask-mobile&pullRequest=14246&issueStatuses=ACCEPTED)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=metamask-mobile&pullRequest=14246&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [98.6% Coverage on New Code](https://sonarcloud.io/component_measures?id=metamask-mobile&pullRequest=14246&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=metamask-mobile&pullRequest=14246&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=metamask-mobile&pullRequest=14246)


author_association: NONE