Skip to content

Commit de22786

Browse files
fix: cp-7.56.0 generate metrics for failed transactions on startup (#20248)
## **Description** Generate metrics for incomplete transactions on startup. Redirect to Perps market view after Perps deposit. ## **Changelog** CHANGELOG entry: null ## **Related issues** Fixes: [#5862](MetaMask/MetaMask-planning#5862) ## **Manual testing steps** ## **Screenshots/Recordings** ### **Before** ### **After** ## **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.
1 parent 35ead26 commit de22786

File tree

6 files changed

+153
-93
lines changed

6 files changed

+153
-93
lines changed

app/components/Views/confirmations/hooks/transactions/useTransactionConfirm.test.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,7 @@ describe('useTransactionConfirm', () => {
300300
});
301301

302302
describe('navigates to', () => {
303-
it('wallet view if perps deposit', async () => {
303+
it('perps market if perps deposit', async () => {
304304
useTransactionMetadataRequestMock.mockReturnValue({
305305
id: transactionIdMock,
306306
type: TransactionType.perpsDeposit,
@@ -310,7 +310,9 @@ describe('useTransactionConfirm', () => {
310310

311311
await result.current.onConfirm();
312312

313-
expect(mockNavigate).toHaveBeenCalledWith(Routes.WALLET_VIEW);
313+
expect(mockNavigate).toHaveBeenCalledWith(Routes.PERPS.ROOT, {
314+
screen: Routes.PERPS.MARKETS,
315+
});
314316
});
315317

316318
it('transactions if full screen', async () => {

app/components/Views/confirmations/hooks/transactions/useTransactionConfirm.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,9 @@ export function useTransactionConfirm() {
8787
);
8888

8989
if (type === TransactionType.perpsDeposit) {
90-
navigation.navigate(Routes.WALLET_VIEW);
90+
navigation.navigate(Routes.PERPS.ROOT, {
91+
screen: Routes.PERPS.MARKETS,
92+
});
9193
} else if (isFullScreenConfirmation) {
9294
navigation.navigate(Routes.TRANSACTIONS_VIEW);
9395
} else {

app/core/Engine/controllers/transaction-controller/event-handlers/metrics.test.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@ jest.mock('../../../../Analytics/MetricsEventBuilder', () => ({
4444
},
4545
}));
4646

47+
jest.mock('../../../Engine', () => ({
48+
context: {},
49+
}));
50+
4751
describe('Transaction Metric Event Handlers', () => {
4852
const mockGetSmartTransactionMetricsProperties = jest.mocked(
4953
getSmartTransactionMetricsProperties,

app/core/Engine/controllers/transaction-controller/event-handlers/metrics.ts

Lines changed: 126 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,11 @@ import type {
1616
TransactionEventHandlerRequest,
1717
TransactionMetricsBuilder,
1818
} from '../types';
19-
import Logger from '../../../../../util/Logger';
2019
import { getMetaMaskPayProperties } from '../event_properties/metamask-pay';
20+
import Engine from '../../../Engine';
21+
import { createProjectLogger } from '@metamask/utils';
22+
23+
const log = createProjectLogger('transaction-metrics');
2124

2225
const METRICS_BUILDERS: TransactionMetricsBuilder[] = [
2326
getMetaMaskPayProperties,
@@ -30,51 +33,57 @@ const createTransactionEventHandler =
3033
transactionMeta: TransactionMeta,
3134
transactionEventHandlerRequest: TransactionEventHandlerRequest,
3235
) => {
33-
const defaultTransactionMetricProperties =
34-
await generateDefaultTransactionMetrics(
35-
eventType,
36-
transactionMeta,
37-
transactionEventHandlerRequest,
38-
);
39-
40-
const metrics = {
41-
properties: defaultTransactionMetricProperties.properties,
42-
sensitiveProperties:
43-
defaultTransactionMetricProperties.sensitiveProperties,
44-
};
36+
try {
37+
const defaultTransactionMetricProperties =
38+
await generateDefaultTransactionMetrics(
39+
eventType,
40+
transactionMeta,
41+
transactionEventHandlerRequest,
42+
);
4543

46-
const allTransactions =
47-
transactionEventHandlerRequest.getState()?.engine?.backgroundState
48-
?.TransactionController?.transactions ?? [];
44+
const metrics = {
45+
properties: defaultTransactionMetricProperties.properties,
46+
sensitiveProperties:
47+
defaultTransactionMetricProperties.sensitiveProperties,
48+
};
4949

50-
const getUIMetrics = getConfirmationMetricProperties.bind(
51-
null,
52-
transactionEventHandlerRequest.getState,
53-
);
50+
const allTransactions =
51+
transactionEventHandlerRequest.getState()?.engine?.backgroundState
52+
?.TransactionController?.transactions ?? [];
5453

55-
const getState = transactionEventHandlerRequest.getState;
54+
const getUIMetrics = getConfirmationMetricProperties.bind(
55+
null,
56+
transactionEventHandlerRequest.getState,
57+
);
5658

57-
for (const builder of METRICS_BUILDERS) {
58-
try {
59-
const currentMetrics = builder({
60-
transactionMeta,
61-
allTransactions,
62-
getUIMetrics,
63-
getState,
64-
});
65-
66-
merge(metrics, currentMetrics);
67-
} catch (error) {
68-
// Intentionally empty
59+
const getState = transactionEventHandlerRequest.getState;
60+
61+
for (const builder of METRICS_BUILDERS) {
62+
try {
63+
const currentMetrics = builder({
64+
transactionMeta,
65+
allTransactions,
66+
getUIMetrics,
67+
getState,
68+
});
69+
70+
merge(metrics, currentMetrics);
71+
} catch (error) {
72+
// Intentionally empty
73+
}
6974
}
70-
}
7175

72-
const event = generateEvent({
73-
...defaultTransactionMetricProperties,
74-
...metrics,
75-
});
76+
const event = generateEvent({
77+
...defaultTransactionMetricProperties,
78+
...metrics,
79+
});
7680

77-
MetaMetrics.getInstance().trackEvent(event);
81+
log('Event', event);
82+
83+
MetaMetrics.getInstance().trackEvent(event);
84+
} catch (error) {
85+
log('Error in transaction event handler', error);
86+
}
7887
};
7988

8089
/**
@@ -125,55 +134,91 @@ export async function handleTransactionFinalizedEventForMetrics(
125134
transactionMeta: TransactionMeta,
126135
transactionEventHandlerRequest: TransactionEventHandlerRequest,
127136
): Promise<void> {
128-
// Generate default properties
129-
const defaultTransactionMetricProperties =
130-
await generateDefaultTransactionMetrics(
131-
TRANSACTION_EVENTS.TRANSACTION_FINALIZED,
132-
transactionMeta,
133-
transactionEventHandlerRequest,
134-
);
135-
136-
// Generate smart transaction properties if applicable
137-
let smartTransactionProperties = { properties: {}, sensitiveProperties: {} };
138137
try {
139-
const { getState, initMessenger, smartTransactionsController } =
140-
transactionEventHandlerRequest;
141-
const shouldUseSmartTransaction = selectShouldUseSmartTransaction(
142-
getState(),
143-
transactionMeta.chainId,
144-
);
145-
if (shouldUseSmartTransaction) {
146-
const smartMetrics = await getSmartTransactionMetricsProperties(
147-
smartTransactionsController,
138+
if (
139+
retryIfEngineNotInitialized(() => {
140+
handleTransactionFinalizedEventForMetrics(
141+
transactionMeta,
142+
transactionEventHandlerRequest,
143+
);
144+
})
145+
) {
146+
return;
147+
}
148+
149+
// Generate default properties
150+
const defaultTransactionMetricProperties =
151+
await generateDefaultTransactionMetrics(
152+
TRANSACTION_EVENTS.TRANSACTION_FINALIZED,
148153
transactionMeta,
149-
true,
150-
initMessenger as unknown as BaseControllerMessenger,
154+
transactionEventHandlerRequest,
151155
);
152-
smartTransactionProperties = {
153-
properties: smartMetrics,
154-
sensitiveProperties: {},
155-
};
156+
157+
// Generate smart transaction properties if applicable
158+
let smartTransactionProperties = {
159+
properties: {},
160+
sensitiveProperties: {},
161+
};
162+
try {
163+
const { getState, initMessenger, smartTransactionsController } =
164+
transactionEventHandlerRequest;
165+
const shouldUseSmartTransaction = selectShouldUseSmartTransaction(
166+
getState(),
167+
transactionMeta.chainId,
168+
);
169+
if (shouldUseSmartTransaction) {
170+
const smartMetrics = await getSmartTransactionMetricsProperties(
171+
smartTransactionsController,
172+
transactionMeta,
173+
true,
174+
initMessenger as unknown as BaseControllerMessenger,
175+
);
176+
smartTransactionProperties = {
177+
properties: smartMetrics,
178+
sensitiveProperties: {},
179+
};
180+
}
181+
} catch (error) {
182+
log('Error getting smart transaction metrics', error);
156183
}
184+
185+
// Add RPC properties
186+
const rpcProperties = generateRPCProperties(transactionMeta.chainId);
187+
188+
// Merge default, smart transaction, and RPC properties
189+
const mergedEventProperties = merge(
190+
{},
191+
defaultTransactionMetricProperties,
192+
smartTransactionProperties,
193+
{
194+
properties: rpcProperties.properties,
195+
sensitiveProperties: rpcProperties.sensitiveProperties,
196+
},
197+
);
198+
199+
// Generate and track the event
200+
const event = generateEvent(mergedEventProperties);
201+
202+
log('Finalized event', event);
203+
204+
MetaMetrics.getInstance().trackEvent(event);
157205
} catch (error) {
158-
Logger.log('Error getting smart transaction metrics:', error);
206+
log('Error in finalized transaction event handler', error);
159207
}
208+
}
160209

161-
// Add RPC properties
162-
const rpcProperties = generateRPCProperties(transactionMeta.chainId);
163-
164-
// Merge default, smart transaction, and RPC properties
165-
const mergedEventProperties = merge(
166-
{},
167-
defaultTransactionMetricProperties,
168-
smartTransactionProperties,
169-
{
170-
properties: rpcProperties.properties,
171-
sensitiveProperties: rpcProperties.sensitiveProperties,
172-
},
173-
);
210+
function retryIfEngineNotInitialized(fn: () => void): boolean {
211+
try {
212+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
213+
const { context } = Engine;
214+
return false;
215+
} catch (e) {
216+
log('Transaction controller event before engine initialized');
174217

175-
// Generate and track the event
176-
const event = generateEvent(mergedEventProperties);
218+
setTimeout(() => {
219+
fn();
220+
}, 5000);
177221

178-
MetaMetrics.getInstance().trackEvent(event);
222+
return true;
223+
}
179224
}

app/core/Engine/controllers/transaction-controller/transaction-controller-init.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,12 @@ export const TransactionControllerInit: ControllerInitFunction<
5858
smartTransactionsController,
5959
} = getControllers(request);
6060

61+
addTransactionControllerListeners({
62+
initMessenger,
63+
getState,
64+
smartTransactionsController,
65+
});
66+
6167
try {
6268
const transactionController: TransactionController =
6369
new TransactionController({
@@ -122,12 +128,6 @@ export const TransactionControllerInit: ControllerInitFunction<
122128
publicKeyEIP7702: AppConstants.EIP_7702_PUBLIC_KEY as Hex | undefined,
123129
});
124130

125-
addTransactionControllerListeners({
126-
initMessenger,
127-
getState,
128-
smartTransactionsController,
129-
});
130-
131131
return { controller: transactionController };
132132
} catch (error) {
133133
Logger.error(error as Error, 'Failed to initialize TransactionController');

app/core/Engine/controllers/transaction-controller/utils.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -175,9 +175,16 @@ export async function generateDefaultTransactionMetrics(
175175

176176
const { from } = txParams || {};
177177

178-
const accountType = isValidHexAddress(from)
179-
? getAddressAccountType(from)
180-
: 'unknown';
178+
let accountType = 'unknown';
179+
180+
// Fails if wallet locked
181+
try {
182+
accountType = isValidHexAddress(from)
183+
? getAddressAccountType(from)
184+
: accountType;
185+
} catch {
186+
// Intentionally empty
187+
}
181188

182189
const batchProperties = await getBatchProperties(transactionMeta);
183190
const gasFeeProperties = getGasMetricProperties(transactionMeta);

0 commit comments

Comments
 (0)