Skip to content

Commit fb0d41c

Browse files
chore(runway): cherry-pick feat(predict): cp-7.65.0 add analytics tracking to deposit flow (#25741)
- feat(predict): cp-7.65.0 add analytics tracking to deposit flow (#25670) <!-- 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 analytics event tracking when deposit is triggered from different entry points (homepage balance, buy preview). The deposit function now accepts optional analytics parameters including entryPoint and amountUsd. - Add trackPredictOrderEvent call in usePredictDeposit when analytics properties are provided - Pass HOMEPAGE_BALANCE entry point from PredictBalance component - Pass BUY_PREVIEW entry point with market context from usePredictPlaceOrder when balance is insufficient - Add new event values: BUY_PREVIEW entry point and transaction types for deposit, withdraw, and claim - Update tests to verify analytics properties are passed correctly <!-- 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? --> ## **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: null ## **Related issues** Fixes: ## **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** <!-- 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** - [ ] 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). - [ ] I've completed the PR template to the best of my ability - [ ] I've included tests if applicable - [ ] I've documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [ ] 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] > **Medium Risk** > Adds analytics tracking to the deposit flow and changes the `deposit` hook signature to accept optional parameters, which could affect callers and analytics payload correctness but does not alter on-chain transaction behavior. > > **Overview** > Adds **analytics tracking for Predict deposits** by extending `usePredictDeposit().deposit` to accept optional `{ amountUsd, analyticsProperties }` and emitting `PredictController.trackPredictOrderEvent` with `status: initiated` and `transactionType: mm_predict_deposit` when properties are provided. > > Updates deposit entry points to pass analytics context: `PredictBalance` now sends `entryPoint: homepage_balance`, and `usePredictPlaceOrder` triggers a deposit on insufficient BUY balance with `entryPoint: buy_preview`, `marketId`, and merged `orderParams.analyticsProperties`. Expands `PredictEventValues` with `BUY_PREVIEW` and new transaction type constants, and updates/adds tests to validate the new analytics payloads. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 4bf856b. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY --> [fa7d510](fa7d510) Co-authored-by: Caainã Jeronimo <caainaje@gmail.com>
1 parent 9d82768 commit fb0d41c

7 files changed

Lines changed: 283 additions & 77 deletions

File tree

app/components/UI/Predict/components/PredictBalance/PredictBalance.test.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,7 @@ describe('PredictBalance', () => {
293293
expect(queryByText(/Withdraw/i)).not.toBeOnTheScreen();
294294
});
295295

296-
it('calls deposit function when Add Funds button is pressed', () => {
296+
it('calls deposit function with analytics properties when Add Funds button is pressed', () => {
297297
// Arrange
298298
const mockDeposit = jest.fn();
299299
mockUsePredictBalance.mockReturnValue({
@@ -318,6 +318,11 @@ describe('PredictBalance', () => {
318318

319319
// Assert
320320
expect(mockDeposit).toHaveBeenCalledTimes(1);
321+
expect(mockDeposit).toHaveBeenCalledWith({
322+
analyticsProperties: {
323+
entryPoint: 'homepage_balance',
324+
},
325+
});
321326
});
322327

323328
it('calls executeGuardedAction when Add Funds button is pressed', () => {

app/components/UI/Predict/components/PredictBalance/PredictBalance.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,11 @@ const PredictBalance: React.FC<PredictBalanceProps> = ({ onLayout }) => {
7070
const handleAddFunds = useCallback(() => {
7171
executeGuardedAction(
7272
() => {
73-
deposit();
73+
deposit({
74+
analyticsProperties: {
75+
entryPoint: PredictEventValues.ENTRY_POINT.HOMEPAGE_BALANCE,
76+
},
77+
});
7478
},
7579
{ attemptedAction: PredictEventValues.ATTEMPTED_ACTION.DEPOSIT },
7680
);

app/components/UI/Predict/constants/eventNames.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,10 +86,14 @@ export const PredictEventValues = {
8686
BACKGROUND: 'background',
8787
TRENDING_SEARCH: 'trending_search',
8888
TRENDING: 'trending',
89+
BUY_PREVIEW: 'buy_preview',
8990
},
9091
TRANSACTION_TYPE: {
9192
MM_PREDICT_BUY: 'mm_predict_buy',
9293
MM_PREDICT_SELL: 'mm_predict_sell',
94+
MM_PREDICT_DEPOSIT: 'mm_predict_deposit',
95+
MM_PREDICT_WITHDRAW: 'mm_predict_withdraw',
96+
MM_PREDICT_CLAIM: 'mm_predict_claim',
9397
},
9498
MARKET_TYPE: {
9599
BINARY: 'binary',

app/components/UI/Predict/hooks/usePredictDeposit.test.ts

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ jest.mock('../../../../core/Engine', () => ({
1919
context: {
2020
PredictController: {
2121
depositWithConfirmation: jest.fn(),
22+
trackPredictOrderEvent: jest.fn(),
2223
},
2324
AccountTreeController: {
2425
getAccountsFromSelectedAccountGroup: jest.fn(() => [
@@ -215,6 +216,9 @@ describe('usePredictDeposit', () => {
215216
(
216217
Engine.context.PredictController.depositWithConfirmation as jest.Mock
217218
).mockClear();
219+
(
220+
Engine.context.PredictController.trackPredictOrderEvent as jest.Mock
221+
).mockClear();
218222
});
219223

220224
afterEach(() => {
@@ -844,4 +848,113 @@ describe('usePredictDeposit', () => {
844848
consoleErrorSpy.mockRestore();
845849
});
846850
});
851+
852+
describe('analytics tracking', () => {
853+
it('tracks analytics event when analyticsProperties is provided', async () => {
854+
(
855+
Engine.context.PredictController.depositWithConfirmation as jest.Mock
856+
).mockResolvedValue({
857+
success: true,
858+
response: { batchId: 'batch-123' },
859+
});
860+
const { result } = setupUsePredictDepositTest();
861+
862+
await result.current.deposit({
863+
amountUsd: 100,
864+
analyticsProperties: {
865+
entryPoint: 'homepage_balance',
866+
},
867+
});
868+
869+
expect(
870+
Engine.context.PredictController.trackPredictOrderEvent,
871+
).toHaveBeenCalledWith({
872+
status: 'initiated',
873+
providerId: 'polymarket',
874+
amountUsd: 100,
875+
analyticsProperties: {
876+
entryPoint: 'homepage_balance',
877+
transactionType: 'mm_predict_deposit',
878+
},
879+
});
880+
});
881+
882+
it('does not track analytics event when analyticsProperties is not provided', async () => {
883+
(
884+
Engine.context.PredictController.depositWithConfirmation as jest.Mock
885+
).mockResolvedValue({
886+
success: true,
887+
response: { batchId: 'batch-123' },
888+
});
889+
const { result } = setupUsePredictDepositTest();
890+
891+
await result.current.deposit();
892+
893+
expect(
894+
Engine.context.PredictController.trackPredictOrderEvent,
895+
).not.toHaveBeenCalled();
896+
});
897+
898+
it('tracks analytics event with custom providerId', async () => {
899+
(
900+
Engine.context.PredictController.depositWithConfirmation as jest.Mock
901+
).mockResolvedValue({
902+
success: true,
903+
response: { batchId: 'batch-123' },
904+
});
905+
const { result } = setupUsePredictDepositTest(
906+
{},
907+
{ providerId: 'custom-provider' },
908+
);
909+
910+
await result.current.deposit({
911+
amountUsd: 50,
912+
analyticsProperties: {
913+
entryPoint: 'buy_preview',
914+
marketId: 'market-123',
915+
},
916+
});
917+
918+
expect(
919+
Engine.context.PredictController.trackPredictOrderEvent,
920+
).toHaveBeenCalledWith({
921+
status: 'initiated',
922+
providerId: 'custom-provider',
923+
amountUsd: 50,
924+
analyticsProperties: {
925+
entryPoint: 'buy_preview',
926+
marketId: 'market-123',
927+
transactionType: 'mm_predict_deposit',
928+
},
929+
});
930+
});
931+
932+
it('tracks analytics event without amountUsd when not provided', async () => {
933+
(
934+
Engine.context.PredictController.depositWithConfirmation as jest.Mock
935+
).mockResolvedValue({
936+
success: true,
937+
response: { batchId: 'batch-123' },
938+
});
939+
const { result } = setupUsePredictDepositTest();
940+
941+
await result.current.deposit({
942+
analyticsProperties: {
943+
entryPoint: 'homepage_balance',
944+
},
945+
});
946+
947+
expect(
948+
Engine.context.PredictController.trackPredictOrderEvent,
949+
).toHaveBeenCalledWith({
950+
status: 'initiated',
951+
providerId: 'polymarket',
952+
amountUsd: undefined,
953+
analyticsProperties: {
954+
entryPoint: 'homepage_balance',
955+
transactionType: 'mm_predict_deposit',
956+
},
957+
});
958+
});
959+
});
847960
});

app/components/UI/Predict/hooks/usePredictDeposit.ts

Lines changed: 102 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { strings } from '../../../../../locales/i18n';
55
import { IconName } from '../../../../component-library/components/Icons/Icon';
66
import { ToastContext } from '../../../../component-library/components/Toast';
77
import { ToastVariants } from '../../../../component-library/components/Toast/Toast.types';
8+
import Engine from '../../../../core/Engine';
89
import Logger from '../../../../util/Logger';
910
import { useAppThemeFromContext } from '../../../../util/theme';
1011
import { ConfirmationLoader } from '../../../Views/confirmations/components/confirm/confirm-component';
@@ -15,11 +16,21 @@ import { PredictNavigationParamList } from '../types/navigation';
1516
import { ensureError } from '../utils/predictErrorHandler';
1617
import { usePredictTrading } from './usePredictTrading';
1718
import { getEvmAccountFromSelectedAccountGroup } from '../utils/accounts';
19+
import {
20+
PredictEventValues,
21+
PredictTradeStatus,
22+
} from '../constants/eventNames';
23+
import { PlaceOrderParams } from '../providers/types';
1824

1925
interface UsePredictDepositParams {
2026
providerId?: string;
2127
}
2228

29+
interface PredictDepositAnalyticsParams {
30+
amountUsd?: number;
31+
analyticsProperties?: PlaceOrderParams['analyticsProperties'];
32+
}
33+
2334
export const usePredictDeposit = ({
2435
providerId = 'polymarket',
2536
}: UsePredictDepositParams = {}) => {
@@ -41,34 +52,74 @@ export const usePredictDeposit = ({
4152
}),
4253
);
4354

44-
const deposit = useCallback(async () => {
45-
try {
46-
navigateToConfirmation({
47-
loader: ConfirmationLoader.CustomAmount,
48-
});
55+
const deposit = useCallback(
56+
async (params?: PredictDepositAnalyticsParams) => {
57+
try {
58+
navigateToConfirmation({
59+
loader: ConfirmationLoader.CustomAmount,
60+
});
4961

50-
depositWithConfirmation({
51-
providerId,
52-
}).catch((err) => {
53-
console.error('Failed to initialize deposit:', err);
62+
const { amountUsd, analyticsProperties } = params ?? {};
5463

55-
// Log error with deposit initialization context
56-
Logger.error(ensureError(err), {
57-
tags: {
58-
feature: PREDICT_CONSTANTS.FEATURE_NAME,
59-
component: 'usePredictDeposit',
60-
},
61-
context: {
62-
name: 'usePredictDeposit',
63-
data: {
64-
method: 'deposit',
65-
action: 'deposit_initialization',
66-
operation: 'financial_operations',
67-
providerId,
64+
if (analyticsProperties) {
65+
Engine.context.PredictController.trackPredictOrderEvent({
66+
status: PredictTradeStatus.INITIATED,
67+
providerId,
68+
amountUsd,
69+
analyticsProperties: {
70+
...analyticsProperties,
71+
transactionType:
72+
PredictEventValues.TRANSACTION_TYPE.MM_PREDICT_DEPOSIT,
6873
},
69-
},
74+
});
75+
}
76+
77+
depositWithConfirmation({
78+
providerId,
79+
}).catch((err) => {
80+
console.error('Failed to initialize deposit:', err);
81+
82+
// Log error with deposit initialization context
83+
Logger.error(ensureError(err), {
84+
tags: {
85+
feature: PREDICT_CONSTANTS.FEATURE_NAME,
86+
component: 'usePredictDeposit',
87+
},
88+
context: {
89+
name: 'usePredictDeposit',
90+
data: {
91+
method: 'deposit',
92+
action: 'deposit_initialization',
93+
operation: 'financial_operations',
94+
providerId,
95+
},
96+
},
97+
});
98+
navigation.goBack();
99+
toastRef?.current?.showToast({
100+
variant: ToastVariants.Icon,
101+
labelOptions: [
102+
{ label: strings('predict.deposit.error_title'), isBold: true },
103+
{ label: '\n', isBold: false },
104+
{
105+
label: strings('predict.deposit.error_description'),
106+
isBold: false,
107+
},
108+
],
109+
iconName: IconName.Error,
110+
iconColor: theme.colors.error.default,
111+
backgroundColor: theme.colors.accent04.normal,
112+
hasNoTimeout: false,
113+
linkButtonOptions: {
114+
label: strings('predict.deposit.try_again'),
115+
onPress: () => deposit(params),
116+
},
117+
});
70118
});
119+
} catch (err) {
120+
console.error('Failed to proceed with deposit:', err);
71121
navigation.goBack();
122+
// Re-throw to allow testing of this error path
72123
toastRef?.current?.showToast({
73124
variant: ToastVariants.Icon,
74125
labelOptions: [
@@ -85,60 +136,38 @@ export const usePredictDeposit = ({
85136
hasNoTimeout: false,
86137
linkButtonOptions: {
87138
label: strings('predict.deposit.try_again'),
88-
onPress: () => deposit(),
139+
onPress: () => deposit(params),
89140
},
90141
});
91-
});
92-
} catch (err) {
93-
console.error('Failed to proceed with deposit:', err);
94-
navigation.goBack();
95-
// Re-throw to allow testing of this error path
96-
toastRef?.current?.showToast({
97-
variant: ToastVariants.Icon,
98-
labelOptions: [
99-
{ label: strings('predict.deposit.error_title'), isBold: true },
100-
{ label: '\n', isBold: false },
101-
{
102-
label: strings('predict.deposit.error_description'),
103-
isBold: false,
104-
},
105-
],
106-
iconName: IconName.Error,
107-
iconColor: theme.colors.error.default,
108-
backgroundColor: theme.colors.accent04.normal,
109-
hasNoTimeout: false,
110-
linkButtonOptions: {
111-
label: strings('predict.deposit.try_again'),
112-
onPress: () => deposit(),
113-
},
114-
});
115142

116-
// Log error with deposit navigation context
117-
Logger.error(ensureError(err), {
118-
tags: {
119-
feature: PREDICT_CONSTANTS.FEATURE_NAME,
120-
component: 'usePredictDeposit',
121-
},
122-
context: {
123-
name: 'usePredictDeposit',
124-
data: {
125-
method: 'deposit',
126-
action: 'deposit_navigation',
127-
operation: 'financial_operations',
128-
providerId,
143+
// Log error with deposit navigation context
144+
Logger.error(ensureError(err), {
145+
tags: {
146+
feature: PREDICT_CONSTANTS.FEATURE_NAME,
147+
component: 'usePredictDeposit',
129148
},
130-
},
131-
});
132-
}
133-
}, [
134-
depositWithConfirmation,
135-
navigateToConfirmation,
136-
navigation,
137-
providerId,
138-
theme.colors.accent04.normal,
139-
theme.colors.error.default,
140-
toastRef,
141-
]);
149+
context: {
150+
name: 'usePredictDeposit',
151+
data: {
152+
method: 'deposit',
153+
action: 'deposit_navigation',
154+
operation: 'financial_operations',
155+
providerId,
156+
},
157+
},
158+
});
159+
}
160+
},
161+
[
162+
depositWithConfirmation,
163+
navigateToConfirmation,
164+
navigation,
165+
providerId,
166+
theme.colors.accent04.normal,
167+
theme.colors.error.default,
168+
toastRef,
169+
],
170+
);
142171

143172
return {
144173
deposit,

0 commit comments

Comments
 (0)