Skip to content

Commit 36b4602

Browse files
authored
feat: add metrics property mm_pay_time_to_complete_s (#27476)
<!-- 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** Add new property for MM Pay transaction metrics `mm_pay_time_to_complete_s`. Which is time from when transaction is submitted to what it is finalized. ## **Changelog** <!-- If this PR is not End-User-Facing and should not show up in the CHANGELOG, you can choose to either: 1. Write `CHANGELOG entry: null` 2. Label with `no-changelog` If this PR is End-User-Facing, please write a short User-Facing description in the past tense like: `CHANGELOG entry: Added a new tab for users to see their NFTs` `CHANGELOG entry: Fixed a bug that was causing some NFTs to flicker` (This helps the Release Engineer do their job more quickly and accurately) --> CHANGELOG entry: ## **Related issues** Fixes: https://consensyssoftware.atlassian.net/browse/CONF-721 ## **Manual testing steps** ```gherkin Feature: my feature name Scenario: user [verb for user action] Given [describe expected initial app state] When user [verb for user action] Then [describe expected outcome] ``` ## **Screenshots/Recordings** NA ## **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. <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Low Risk** > Low risk analytics-only change that adds a derived timing property on `TRANSACTION_FINALIZED` events; main risk is incorrect/undefined `submittedTime` leading to missing or skewed metrics. > > **Overview** > Adds a new MetaMetrics property, `mm_pay_time_to_complete_s`, to MetaMask Pay transaction metrics, computed as seconds from `submittedTime` to `TRANSACTION_FINALIZED`. > > The metric is emitted for both parent pay transactions and child bridge/swap steps (using the parent transaction’s `submittedTime`), with unit tests covering finalized vs non-finalized events and missing timestamps. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit b3f5da6. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY -->
1 parent 9f52a9c commit 36b4602

2 files changed

Lines changed: 95 additions & 0 deletions

File tree

app/core/Engine/controllers/transaction-controller/metrics_properties/metamask-pay.test.ts

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -484,4 +484,75 @@ describe('Metamask Pay Metrics', () => {
484484
sensitiveProperties: {},
485485
});
486486
});
487+
488+
describe('mm_pay_time_to_complete_s', () => {
489+
afterEach(() => {
490+
jest.restoreAllMocks();
491+
});
492+
493+
it('adds mm_pay_time_to_complete_s for finalized parent MM Pay transaction', () => {
494+
jest.spyOn(Date, 'now').mockReturnValue(1060500);
495+
496+
request.transactionMeta.type = TransactionType.perpsDeposit;
497+
request.transactionMeta.submittedTime = 1000000;
498+
499+
const result = getMetaMaskPayProperties(request) as TransactionMetrics;
500+
501+
expect(result.properties).toStrictEqual(
502+
expect.objectContaining({
503+
mm_pay_time_to_complete_s: 60.5,
504+
}),
505+
);
506+
});
507+
508+
it('adds mm_pay_time_to_complete_s for finalized child transaction using parent submittedTime', () => {
509+
jest.spyOn(Date, 'now').mockReturnValue(2045123);
510+
511+
request.allTransactions = [
512+
{
513+
id: 'parent-1',
514+
type: TransactionType.perpsDeposit,
515+
requiredTransactionIds: ['child-1'],
516+
submittedTime: 2000000,
517+
} as TransactionMeta,
518+
];
519+
520+
const result = getMetaMaskPayProperties(request) as TransactionMetrics;
521+
522+
expect(result.properties).toStrictEqual(
523+
expect.objectContaining({
524+
mm_pay_time_to_complete_s: 45.123,
525+
}),
526+
);
527+
});
528+
529+
it('does not add mm_pay_time_to_complete_s for non-finalized events', () => {
530+
request.eventType = TRANSACTION_EVENTS.TRANSACTION_SUBMITTED;
531+
request.transactionMeta.type = TransactionType.perpsDeposit;
532+
request.transactionMeta.submittedTime = 1000000;
533+
534+
const result = getMetaMaskPayProperties(request) as TransactionMetrics;
535+
536+
expect(result.properties).not.toHaveProperty('mm_pay_time_to_complete_s');
537+
});
538+
539+
it('does not add mm_pay_time_to_complete_s when submittedTime is undefined', () => {
540+
request.transactionMeta.type = TransactionType.perpsDeposit;
541+
542+
const result = getMetaMaskPayProperties(request) as TransactionMetrics;
543+
544+
expect(result.properties).not.toHaveProperty('mm_pay_time_to_complete_s');
545+
});
546+
547+
it('does not add mm_pay_time_to_complete_s for non-MM-Pay transactions', () => {
548+
jest.spyOn(Date, 'now').mockReturnValue(1060000);
549+
550+
request.transactionMeta.type = TransactionType.contractInteraction;
551+
request.transactionMeta.submittedTime = 1000000;
552+
553+
const result = getMetaMaskPayProperties(request) as TransactionMetrics;
554+
555+
expect(result.properties).not.toHaveProperty('mm_pay_time_to_complete_s');
556+
});
557+
});
487558
});

app/core/Engine/controllers/transaction-controller/metrics_properties/metamask-pay.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
import { RootState } from '../../../../../reducers';
1616
import { selectSingleTokenByAddressAndChainId } from '../../../../../selectors/tokensController';
1717
import { Hex } from '@metamask/utils';
18+
import { TRANSACTION_EVENTS } from '../../../../Analytics/events/confirmations';
1819

1920
const FOUR_BYTE_SAFE_PROXY_CREATE = '0xa1884d2c';
2021

@@ -34,6 +35,7 @@ const PAY_TYPES = [
3435
];
3536

3637
export const getMetaMaskPayProperties: TransactionMetricsBuilder = ({
38+
eventType,
3739
transactionMeta,
3840
allTransactions,
3941
getUIMetrics,
@@ -58,6 +60,10 @@ export const getMetaMaskPayProperties: TransactionMetricsBuilder = ({
5860
if (hasTransactionType(transactionMeta, PAY_TYPES) || !parentTransaction) {
5961
addFallbackProperties(properties, transactionMeta, getState());
6062

63+
if (hasTransactionType(transactionMeta, PAY_TYPES) || properties.mm_pay) {
64+
addTimeToComplete(properties, eventType, transactionMeta.submittedTime);
65+
}
66+
6167
return {
6268
properties,
6369
sensitiveProperties,
@@ -127,12 +133,30 @@ export const getMetaMaskPayProperties: TransactionMetricsBuilder = ({
127133
}
128134
}
129135

136+
addTimeToComplete(properties, eventType, parentTransaction.submittedTime);
137+
130138
return {
131139
properties,
132140
sensitiveProperties,
133141
};
134142
};
135143

144+
function addTimeToComplete(
145+
properties: JsonMap,
146+
eventType: Parameters<TransactionMetricsBuilder>[0]['eventType'],
147+
submittedTime: number | undefined,
148+
) {
149+
if (
150+
eventType !== TRANSACTION_EVENTS.TRANSACTION_FINALIZED ||
151+
typeof submittedTime !== 'number'
152+
) {
153+
return;
154+
}
155+
156+
properties.mm_pay_time_to_complete_s =
157+
Math.round(Date.now() - submittedTime) / 1000;
158+
}
159+
136160
function addFallbackProperties(
137161
properties: JsonMap,
138162
transaction: TransactionMeta,

0 commit comments

Comments
 (0)