From 67403d578cd6e950214852e0c3a510ad65652818 Mon Sep 17 00:00:00 2001 From: Jyoti Puri Date: Tue, 26 Nov 2024 15:34:52 +0530 Subject: [PATCH] Adding basic metrics for decoding signatures --- .../permit-simulation.test.tsx | 20 +++++++ .../permit-simulation/permit-simulation.tsx | 3 + .../useDecodedSignatureMetrics.test.ts | 55 +++++++++++++++++++ .../useDecodedSignatureMetrics.ts | 45 +++++++++++++++ 4 files changed, 123 insertions(+) create mode 100644 ui/pages/confirmations/components/simulation-details/useDecodedSignatureMetrics.test.ts create mode 100644 ui/pages/confirmations/components/simulation-details/useDecodedSignatureMetrics.ts diff --git a/ui/pages/confirmations/components/confirm/info/typed-sign/permit-simulation/permit-simulation.test.tsx b/ui/pages/confirmations/components/confirm/info/typed-sign/permit-simulation/permit-simulation.test.tsx index d0183d1ac2e7..50ed70d7625f 100644 --- a/ui/pages/confirmations/components/confirm/info/typed-sign/permit-simulation/permit-simulation.test.tsx +++ b/ui/pages/confirmations/components/confirm/info/typed-sign/permit-simulation/permit-simulation.test.tsx @@ -7,6 +7,7 @@ import { getMockTypedSignConfirmStateForRequest } from '../../../../../../../../ import { renderWithConfirmContextProvider } from '../../../../../../../../test/lib/confirmations/render-helpers'; import { permitSignatureMsg } from '../../../../../../../../test/data/confirmations/typed_sign'; import { memoizedGetTokenStandardAndDetails } from '../../../../../utils/token'; +import * as SignatureMetrics from '../../../../simulation-details/useDecodedSignatureMetrics'; import PermitSimulation from './permit-simulation'; jest.mock('../../../../../../../store/actions', () => { @@ -44,6 +45,25 @@ describe('PermitSimulation', () => { }); }); + it('should call hook to register signature metrics properties', async () => { + const state = getMockTypedSignConfirmStateForRequest({ + ...permitSignatureMsg, + decodingLoading: false, + decodingData: undefined, + }); + const mockStore = configureMockStore([])(state); + + const mockedUseDecodedSignatureMetrics = jest + .spyOn(SignatureMetrics, 'useDecodedSignatureMetrics') + .mockImplementation(() => ''); + + await act(async () => { + renderWithConfirmContextProvider(, mockStore); + + expect(mockedUseDecodedSignatureMetrics).toHaveBeenCalledTimes(1); + }); + }); + it('should render default simulation if decoding api returns error', async () => { const state = getMockTypedSignConfirmStateForRequest({ ...permitSignatureMsg, diff --git a/ui/pages/confirmations/components/confirm/info/typed-sign/permit-simulation/permit-simulation.tsx b/ui/pages/confirmations/components/confirm/info/typed-sign/permit-simulation/permit-simulation.tsx index 86055425fa46..fb255794a59a 100644 --- a/ui/pages/confirmations/components/confirm/info/typed-sign/permit-simulation/permit-simulation.tsx +++ b/ui/pages/confirmations/components/confirm/info/typed-sign/permit-simulation/permit-simulation.tsx @@ -2,6 +2,7 @@ import React from 'react'; import { SignatureRequestType } from '../../../../../types/confirm'; import { useConfirmContext } from '../../../../../context/confirm'; +import { useDecodedSignatureMetrics } from '../../../../simulation-details/useDecodedSignatureMetrics'; import { DefaultSimulation } from './default-simulation'; import { DecodedSimulation } from './decoded-simulation'; @@ -9,6 +10,8 @@ const PermitSimulation: React.FC = () => { const { currentConfirmation } = useConfirmContext(); const { decodingLoading, decodingData } = currentConfirmation; + useDecodedSignatureMetrics(); + if ( decodingData?.error || (decodingData?.stateChanges === undefined && decodingLoading !== true) diff --git a/ui/pages/confirmations/components/simulation-details/useDecodedSignatureMetrics.test.ts b/ui/pages/confirmations/components/simulation-details/useDecodedSignatureMetrics.test.ts new file mode 100644 index 000000000000..381e4d5ef466 --- /dev/null +++ b/ui/pages/confirmations/components/simulation-details/useDecodedSignatureMetrics.test.ts @@ -0,0 +1,55 @@ +import { getMockTypedSignConfirmStateForRequest } from '../../../../../test/data/confirmations/helper'; +import { permitSignatureMsg } from '../../../../../test/data/confirmations/typed_sign'; +import { renderHookWithConfirmContextProvider } from '../../../../../test/lib/confirmations/render-helpers'; +import * as SignatureEventFragment from '../../hooks/useSignatureEventFragment'; +import { useDecodedSignatureMetrics } from './useDecodedSignatureMetrics'; + +describe('useDecodedSignatureMetrics', () => { + it('should not call updateSignatureEventFragment if decodingLoading is true', async () => { + const state = getMockTypedSignConfirmStateForRequest({ + ...permitSignatureMsg, + decodingLoading: true, + }); + + const mockUpdateSignatureEventFragment = jest.fn(); + jest + .spyOn(SignatureEventFragment, 'useSignatureEventFragment') + .mockImplementation(() => ({ + updateSignatureEventFragment: mockUpdateSignatureEventFragment, + })); + + renderHookWithConfirmContextProvider( + () => useDecodedSignatureMetrics(), + state, + ); + + expect(mockUpdateSignatureEventFragment).toHaveBeenCalledTimes(0); + }); + + it('should call updateSignatureEventFragment with correct parameters', async () => { + const state = getMockTypedSignConfirmStateForRequest({ + ...permitSignatureMsg, + decodingLoading: false, + }); + + const mockUpdateSignatureEventFragment = jest.fn(); + jest + .spyOn(SignatureEventFragment, 'useSignatureEventFragment') + .mockImplementation(() => ({ + updateSignatureEventFragment: mockUpdateSignatureEventFragment, + })); + + renderHookWithConfirmContextProvider( + () => useDecodedSignatureMetrics(), + state, + ); + + expect(mockUpdateSignatureEventFragment).toHaveBeenCalledTimes(1); + expect(mockUpdateSignatureEventFragment).toHaveBeenLastCalledWith({ + properties: { + decoding_change_types: [], + decoding_response: 'NO_CHANGE', + }, + }); + }); +}); diff --git a/ui/pages/confirmations/components/simulation-details/useDecodedSignatureMetrics.ts b/ui/pages/confirmations/components/simulation-details/useDecodedSignatureMetrics.ts new file mode 100644 index 000000000000..28dd21785688 --- /dev/null +++ b/ui/pages/confirmations/components/simulation-details/useDecodedSignatureMetrics.ts @@ -0,0 +1,45 @@ +import { useEffect } from 'react'; +import { DecodingDataStateChange } from '@metamask/signature-controller'; + +import { SignatureRequestType } from '../../types/confirm'; +import { useConfirmContext } from '../../context/confirm'; +import { useSignatureEventFragment } from '../../hooks/useSignatureEventFragment'; + +enum DecodingResponse { + Change = 'CHANGE', + NoChange = 'NO_CHANGE', +} + +export function useDecodedSignatureMetrics() { + const { updateSignatureEventFragment } = useSignatureEventFragment(); + const { currentConfirmation } = useConfirmContext(); + const { decodingLoading, decodingData } = currentConfirmation; + + const decodingChangeTypes = (decodingData?.stateChanges ?? []).map( + (change: DecodingDataStateChange) => change.changeType, + ); + + const decodingResponse = + decodingData?.error?.type ?? + (decodingChangeTypes.length + ? DecodingResponse.Change + : DecodingResponse.NoChange); + + useEffect(() => { + if (decodingLoading) { + return; + } + + updateSignatureEventFragment({ + properties: { + decoding_response: decodingResponse, + decoding_change_types: decodingChangeTypes, + }, + }); + }, [ + decodingResponse, + decodingLoading, + decodingChangeTypes, + updateSignatureEventFragment, + ]); +}