Skip to content

Commit c4049aa

Browse files
authored
fix: metrics functionality fixes for money account transactions (#29723)
<!-- Please submit this PR as a draft initially. Do not mark it as "Ready for review" until this PR meets the canonical Definition of Ready For Review in `docs/readme/ready-for-review.md`. In short: the template must be materially complete (not just section titles present), all status checks must be currently passing, and the only expected follow-up commits must be reviewer-driven. --> ## **Description** Metrics functionality fixes for money account transactions ## **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-1341 ## **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** <!-- Every checklist item must be consciously assessed before marking this PR as "Ready for review". A checked box means you deliberately considered that responsibility, not that you literally performed every action listed. Unchecked boxes are ambiguous: they are not an implicit "N/A" and they are not a silent "skip". See `docs/readme/ready-for-review.md` for the full checklist semantics. --> - [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. #### Performance checks (if applicable) - [ ] I've tested on Android - Ideally on a mid-range device; emulator is acceptable - [ ] I've tested with a power user scenario - Use these [power-user SRPs](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/edit-v2/401401446401?draftShareId=9d77e1e1-4bdc-4be1-9ebb-ccd916988d93) to import wallets with many accounts and tokens - [ ] I've instrumented key operations with Sentry traces for production performance metrics - See [`trace()`](/app/util/trace.ts) for usage and [`addToken`](/app/components/Views/AddAsset/components/AddCustomToken/AddCustomToken.tsx#L274) for an example For performance guidelines and tooling, see the [Performance Guide](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/400085549067/Performance+Guide+for+Engineers). ## **Pre-merge reviewer checklist** <!-- Reviewer checklist items follow the same semantics as the author checklist: an unchecked box is ambiguous, a checked box means the reviewer consciously assessed that responsibility. See `docs/readme/ready-for-review.md`. --> - [ ] 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] > **Medium Risk** > Updates transaction-type classification for metrics and activity filtering, which could affect analytics attribution and visibility of transactions in activity feeds if mis-tagged. Changes are mostly additive and covered by new unit tests, but touch shared transaction metrics utilities. > > **Overview** > Adds *money account* support to the transaction metrics pipeline by introducing `MonetizedPrimitive.MoneyAccount`, mapping `moneyAccountDeposit`/`moneyAccountWithdraw` in `getMonetizedPrimitive`, and ensuring base `transaction_type` string derivation returns `money_account_deposit|withdraw` (including when nested). > > Updates MM Pay metrics to treat money account deposit/withdraw as PAY types and derive `mm_pay_use_case` accordingly, and adjusts the confirmation UI metrics hook to include `simulation_sending_assets_total_value` for money account **deposits** (but not withdraws). Also expands staged A/B test attribution and activity `PAY_TYPES` filtering to include money account transactions, with accompanying test coverage. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit bac530a. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
1 parent 9432aa0 commit c4049aa

11 files changed

Lines changed: 170 additions & 1 deletion

File tree

app/components/Views/confirmations/hooks/pay/useTransactionPayMetrics.test.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,48 @@ describe('useTransactionPayMetrics', () => {
234234
});
235235
});
236236

237+
it('includes simulation_sending_assets_total_value for money account deposit', async () => {
238+
useTransactionPayTokenMock.mockReturnValue({
239+
payToken: PAY_TOKEN_MOCK,
240+
setPayToken: noop,
241+
} as ReturnType<typeof useTransactionPayToken>);
242+
243+
runHook({ type: TransactionType.moneyAccountDeposit });
244+
245+
await act(async () => noop());
246+
247+
expect(updateConfirmationMetricMock).toHaveBeenCalledWith({
248+
id: transactionIdMock,
249+
params: {
250+
properties: expect.objectContaining({
251+
simulation_sending_assets_total_value: 1.23,
252+
}),
253+
sensitiveProperties: {},
254+
},
255+
});
256+
});
257+
258+
it('omits simulation_sending_assets_total_value for money account withdraw', async () => {
259+
useTransactionPayTokenMock.mockReturnValue({
260+
payToken: PAY_TOKEN_MOCK,
261+
setPayToken: noop,
262+
} as ReturnType<typeof useTransactionPayToken>);
263+
264+
runHook({ type: TransactionType.moneyAccountWithdraw });
265+
266+
await act(async () => noop());
267+
268+
const calledProps = (
269+
updateConfirmationMetricMock.mock.calls[0]?.[0] as {
270+
params: { properties: Record<string, unknown> };
271+
}
272+
)?.params?.properties;
273+
274+
expect(calledProps).not.toHaveProperty(
275+
'simulation_sending_assets_total_value',
276+
);
277+
});
278+
237279
describe('mm_pay_quote_requested', () => {
238280
it('is false initially', async () => {
239281
useTransactionPayTokenMock.mockReturnValue({

app/components/Views/confirmations/hooks/pay/useTransactionPayMetrics.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,10 @@ export function useTransactionPayMetrics() {
8787
if (
8888
payToken &&
8989
(hasTransactionType(transactionMeta, [TransactionType.perpsDeposit]) ||
90-
hasTransactionType(transactionMeta, [TransactionType.predictDeposit]))
90+
hasTransactionType(transactionMeta, [TransactionType.predictDeposit]) ||
91+
hasTransactionType(transactionMeta, [
92+
TransactionType.moneyAccountDeposit,
93+
]))
9194
) {
9295
properties.simulation_sending_assets_total_value = sendingValue;
9396
}

app/core/Analytics/MetaMetrics.types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@ export enum MonetizedPrimitive {
161161
Ramps = 'ramps',
162162
Predict = 'predict',
163163
MmPay = 'mm_pay',
164+
MoneyAccount = 'money_account',
164165
}
165166

166167
/**

app/core/Analytics/events/transactions/utils.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ export function getMonetizedPrimitive(
2323
case TransactionType.predictWithdraw:
2424
case TransactionType.predictClaim:
2525
return MonetizedPrimitive.Predict;
26+
case TransactionType.moneyAccountDeposit:
27+
case TransactionType.moneyAccountWithdraw:
28+
return MonetizedPrimitive.MoneyAccount;
2629
default:
2730
return undefined;
2831
}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,8 @@ describe('getTransactionTypeValue', () => {
104104
['predict_deposit', TransactionType.predictDeposit],
105105
['predict_withdraw', TransactionType.predictWithdraw],
106106
['perps_withdraw', TransactionType.perpsWithdraw],
107+
['money_account_deposit', TransactionType.moneyAccountDeposit],
108+
['money_account_withdraw', TransactionType.moneyAccountWithdraw],
107109
['musd_conversion', TransactionType.musdConversion],
108110
['musd_claim', TransactionType.musdClaim],
109111
])('returns %s if nested transaction type is %s', (expected, nestedType) => {

app/core/Engine/controllers/transaction-controller/metrics_properties/base.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,18 @@ export function getTransactionTypeValue(
7474
return 'predict_claim';
7575
}
7676

77+
if (
78+
hasTransactionType(transactionMeta, [TransactionType.moneyAccountDeposit])
79+
) {
80+
return 'money_account_deposit';
81+
}
82+
83+
if (
84+
hasTransactionType(transactionMeta, [TransactionType.moneyAccountWithdraw])
85+
) {
86+
return 'money_account_withdraw';
87+
}
88+
7789
if (hasTransactionType(transactionMeta, [TransactionType.musdConversion])) {
7890
return 'musd_conversion';
7991
}

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

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,75 @@ describe('Metamask Pay Metrics', () => {
8383
});
8484
});
8585

86+
it.each([
87+
TransactionType.moneyAccountDeposit,
88+
TransactionType.moneyAccountWithdraw,
89+
])('returns nothing if %s without controller state', (type) => {
90+
request.transactionMeta.type = type;
91+
92+
const result = getMetaMaskPayProperties(request);
93+
94+
expect(result).toStrictEqual({
95+
properties: {},
96+
sensitiveProperties: {},
97+
});
98+
});
99+
100+
it.each([
101+
[TransactionType.moneyAccountDeposit, 'money_account_deposit'],
102+
[TransactionType.moneyAccountWithdraw, 'money_account_withdraw'],
103+
])(
104+
'derives mm_pay_use_case=%s for %s parent',
105+
(parentType, expectedUseCase) => {
106+
getStateMock.mockReturnValue({
107+
engine: {
108+
backgroundState: {
109+
TokensController: { allTokens: {} },
110+
TransactionPayController: {
111+
transactionData: {
112+
'parent-1': {
113+
paymentToken: { symbol: 'USDC', chainId: '0x1' },
114+
quotes: [{ strategy: TransactionPayStrategy.Relay }],
115+
tokens: [{ skipIfBalance: false, amountUsd: '50' }],
116+
totals: {
117+
targetAmount: { usd: '49.5', fiat: '49.5' },
118+
fees: {
119+
metaMask: { usd: '0', fiat: '0' },
120+
provider: { usd: '0.2', fiat: '0.2' },
121+
sourceNetwork: { estimate: { usd: '0.1', fiat: '0.1' } },
122+
targetNetwork: { usd: '0', fiat: '0' },
123+
},
124+
},
125+
},
126+
},
127+
},
128+
},
129+
},
130+
} as never);
131+
132+
request.allTransactions = [
133+
{
134+
id: 'parent-1',
135+
type: parentType,
136+
metamaskPay: { chainId: '0x1', tokenAddress: '0xA0b8' },
137+
requiredTransactionIds: ['child-1'],
138+
} as unknown as TransactionMeta,
139+
];
140+
141+
const result = getMetaMaskPayProperties(request);
142+
143+
expect(result).toStrictEqual({
144+
properties: expect.objectContaining({
145+
mm_pay: true,
146+
mm_pay_use_case: expectedUseCase,
147+
mm_pay_token_selected: 'USDC',
148+
mm_pay_chain_selected: '0x1',
149+
}),
150+
sensitiveProperties: {},
151+
});
152+
},
153+
);
154+
86155
it('derives parent mm_pay_* properties for child transaction from controller state', () => {
87156
getStateMock.mockReturnValue({
88157
engine: {

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ import { BigNumber } from 'bignumber.js';
2020
const FOUR_BYTE_SAFE_PROXY_CREATE = '0xa1884d2c';
2121

2222
const PAY_TYPES = [
23+
TransactionType.moneyAccountDeposit,
24+
TransactionType.moneyAccountWithdraw,
2325
TransactionType.perpsDeposit,
2426
TransactionType.perpsWithdraw,
2527
TransactionType.predictDeposit,
@@ -31,6 +33,8 @@ const USE_CASE_MAP: [TransactionType[], string][] = [
3133
[[TransactionType.predictDeposit], 'predict_deposit'],
3234
[[TransactionType.perpsDeposit], 'perps_deposit'],
3335
[[TransactionType.perpsWithdraw], 'perps_withdraw'],
36+
[[TransactionType.moneyAccountDeposit], 'money_account_deposit'],
37+
[[TransactionType.moneyAccountWithdraw], 'money_account_withdraw'],
3438
];
3539

3640
export const getMetaMaskPayProperties: TransactionMetricsBuilder = ({

app/core/Engine/controllers/transaction-controller/metrics_properties/swap-transaction-ab-tests.test.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,4 +108,34 @@ describe('getSwapTransactionActiveAbTestProperties', () => {
108108
sensitiveProperties: {},
109109
});
110110
});
111+
112+
it.each([
113+
TransactionType.moneyAccountDeposit,
114+
TransactionType.moneyAccountWithdraw,
115+
])('returns active_ab_tests for %s Transaction Added', (type) => {
116+
const abTests = [
117+
{ key: 'homeTMCU470AbtestTrendingSections', value: 'trendingSections' },
118+
];
119+
registerTransactionAbTestAttributionForIds([TX_ID], abTests);
120+
const request = createMockRequest({
121+
transactionMeta: {
122+
id: TX_ID,
123+
type,
124+
} as never,
125+
});
126+
127+
expect(getSwapTransactionActiveAbTestProperties(request)).toEqual({
128+
properties: {
129+
active_ab_tests: [
130+
{
131+
key: 'homeTMCU470AbtestTrendingSections',
132+
value: 'trendingSections',
133+
key_value_pair:
134+
'homeTMCU470AbtestTrendingSections=trendingSections',
135+
},
136+
],
137+
},
138+
sensitiveProperties: {},
139+
});
140+
});
111141
});

app/core/Engine/controllers/transaction-controller/metrics_properties/swap-transaction-ab-tests.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ const TRANSACTION_TYPES_FOR_ACTIVE_AB_TESTS: ReadonlySet<TransactionType> =
1818
TransactionType.swapApproval,
1919
TransactionType.swapAndSend,
2020
TransactionType.bridgeApproval,
21+
TransactionType.moneyAccountDeposit,
22+
TransactionType.moneyAccountWithdraw,
2123
TransactionType.perpsAcrossDeposit,
2224
TransactionType.perpsDeposit,
2325
TransactionType.perpsDepositAndOrder,

0 commit comments

Comments
 (0)