Skip to content

Commit 62ba62f

Browse files
authored
feat(analytics): add sanitized origin to sentinel metadata (MetaMask#25612)
Add sanitized origin to sentinel metadata <!-- 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** <!-- 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: Add sanitized origin to sentinel metadata ## **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 a new `origin` field to smart-transaction/relay metadata and bumps `@metamask/smart-transactions-controller`, which could affect downstream ingestion or controller behavior if payload expectations differ. > > **Overview** > Adds `sanitizeOrigin` to normalize transaction origins for analytics (URL origins reduced to hostname; internal origins preserved). > > Smart transaction submission (`smart-publish-hook`) and the EIP-7702 relay publish hook now attach `origin: sanitizeOrigin(transactionMeta.origin)` alongside existing `txType`/`client` metadata, with new unit tests covering URL/internal/empty cases. Also bumps `@metamask/smart-transactions-controller` to `^22.4.0`. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 7c6d2ef. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY -->
1 parent 9642300 commit 62ba62f

6 files changed

Lines changed: 93 additions & 9 deletions

File tree

app/constants/smartTransactions.test.ts

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import { NETWORKS_CHAIN_ID } from './network';
22
import { isProduction } from '../util/environment';
3-
import { getAllowedSmartTransactionsChainIds } from './smartTransactions';
3+
import {
4+
getAllowedSmartTransactionsChainIds,
5+
sanitizeOrigin,
6+
} from './smartTransactions';
47

58
jest.mock('../util/environment', () => ({
69
isProduction: jest.fn(() => false), // Initially mock isProduction to return false
@@ -42,4 +45,50 @@ describe('smartTransactions', () => {
4245
]);
4346
});
4447
});
48+
49+
describe('sanitizeOrigin', () => {
50+
it('extracts hostname from URL with path', () => {
51+
expect(sanitizeOrigin('https://uniswap.org/swap?token=0x123')).toBe(
52+
'uniswap.org',
53+
);
54+
});
55+
56+
it('extracts hostname from URL with subdomain', () => {
57+
expect(sanitizeOrigin('https://app.aave.com/#/markets')).toBe(
58+
'app.aave.com',
59+
);
60+
});
61+
62+
it('extracts hostname from URL with port', () => {
63+
expect(sanitizeOrigin('http://localhost:3000/test')).toBe('localhost');
64+
});
65+
66+
it('returns internal origin as-is', () => {
67+
expect(sanitizeOrigin('metamask')).toBe('metamask');
68+
});
69+
70+
it('returns MetaMask Mobile origin as-is', () => {
71+
expect(sanitizeOrigin('MetaMask Mobile')).toBe('MetaMask Mobile');
72+
});
73+
74+
it('returns RAMPS_SEND origin as-is', () => {
75+
expect(sanitizeOrigin('RAMPS_SEND')).toBe('RAMPS_SEND');
76+
});
77+
78+
it('returns WalletConnect origin as-is', () => {
79+
expect(sanitizeOrigin('wc::')).toBe('wc::');
80+
});
81+
82+
it('returns SDK origin as-is', () => {
83+
expect(sanitizeOrigin('MMSDKREMOTE::abc123')).toBe('MMSDKREMOTE::abc123');
84+
});
85+
86+
it('returns undefined for undefined input', () => {
87+
expect(sanitizeOrigin(undefined)).toBeUndefined();
88+
});
89+
90+
it('returns empty string for empty string input', () => {
91+
expect(sanitizeOrigin('')).toBeUndefined();
92+
});
93+
});
4594
});

app/constants/smartTransactions.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,31 @@ const CLIENT_ID_ANDROID = 'mobileAndroid';
1010
export const getClientForTransactionMetadata = (): string =>
1111
Device.isIos() ? CLIENT_ID_IOS : CLIENT_ID_ANDROID;
1212

13+
/**
14+
* Sanitizes transaction origin for smart transaction analytics.
15+
* - For URL origins (dApps): extracts hostname only
16+
* - For internal origins: returns as-is
17+
* NOTE: This is temporary and will be deprecated once we have a proper feature tracking system.
18+
*
19+
* @param origin - The transaction origin to sanitize
20+
* @returns The sanitized origin (hostname for URLs, original value otherwise)
21+
*/
22+
export const sanitizeOrigin = (origin?: string): string | undefined => {
23+
if (!origin) {
24+
return undefined;
25+
}
26+
27+
try {
28+
// Attempt to parse as URL - will throw for non-URL strings
29+
const url = new URL(origin);
30+
// If hostname is empty, the input wasn't a real URL (e.g., 'wc::', 'MMSDKREMOTE::')
31+
return url.hostname || origin;
32+
} catch {
33+
// Not a valid URL (internal origins like 'metamask', 'RAMPS_SEND')
34+
return origin;
35+
}
36+
};
37+
1338
// TODO: deprecate this and use the feature flags instead
1439
const ALLOWED_SMART_TRANSACTIONS_CHAIN_IDS_DEVELOPMENT: Hex[] = [
1540
NETWORKS_CHAIN_ID.MAINNET,

app/util/smart-transactions/smart-publish-hook.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,10 @@ import { addSwapsTransaction } from '../swaps/swaps-transactions';
3030
import { Hex } from '@metamask/utils';
3131
import { getTransactionById, isLegacyTransaction } from '../transactions';
3232
import { ORIGIN_METAMASK } from '@metamask/controller-utils';
33-
import { getClientForTransactionMetadata } from '../../constants/smartTransactions';
33+
import {
34+
getClientForTransactionMetadata,
35+
sanitizeOrigin,
36+
} from '../../constants/smartTransactions';
3437

3538
type AllowedActions = never;
3639

@@ -447,6 +450,7 @@ class SmartTransactionHook {
447450
signedTx.metadata = {
448451
txType: transactionMeta.type,
449452
client: getClientForTransactionMetadata(),
453+
origin: sanitizeOrigin(transactionMeta.origin),
450454
};
451455
}
452456
return signedTx;
@@ -459,6 +463,7 @@ class SmartTransactionHook {
459463
metadata: {
460464
txType: this.#transactionMeta.type,
461465
client: getClientForTransactionMetadata(),
466+
origin: sanitizeOrigin(this.#transactionMeta.origin),
462467
},
463468
},
464469
];
@@ -472,6 +477,7 @@ class SmartTransactionHook {
472477
metadata: {
473478
txType: this.#transactionMeta.type,
474479
client: getClientForTransactionMetadata(),
480+
origin: sanitizeOrigin(this.#transactionMeta.origin),
475481
},
476482
}));
477483
}

app/util/transactions/hooks/delegation-7702-publish.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,10 @@ import {
4040
import { NetworkClientId } from '@metamask/network-controller';
4141
import { toHex } from '@metamask/controller-utils';
4242
import { isE2ETest, stripSingleLeadingZero } from '../util';
43-
import { getClientForTransactionMetadata } from '../../../constants/smartTransactions';
43+
import {
44+
getClientForTransactionMetadata,
45+
sanitizeOrigin,
46+
} from '../../../constants/smartTransactions';
4447

4548
// Test chain ID (Sepolia) used in E2E tests to match the delegation package's test contract configuration
4649
const SEPOLIA_CHAIN_ID = '0xaa36a7';
@@ -197,6 +200,7 @@ export class Delegation7702PublishHook {
197200
metadata: {
198201
txType: transactionMeta.type,
199202
client: getClientForTransactionMetadata(),
203+
origin: sanitizeOrigin(transactionMeta.origin),
200204
},
201205
};
202206

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,7 @@
285285
"@metamask/selected-network-controller": "^25.0.0",
286286
"@metamask/signature-controller": "^35.0.0",
287287
"@metamask/slip44": "^4.2.0",
288-
"@metamask/smart-transactions-controller": "^22.3.0",
288+
"@metamask/smart-transactions-controller": "^22.4.0",
289289
"@metamask/snaps-controllers": "^17.2.1",
290290
"@metamask/snaps-execution-environments": "^10.4.1",
291291
"@metamask/snaps-rpc-methods": "^14.2.0",

yarn.lock

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9308,9 +9308,9 @@ __metadata:
93089308
languageName: node
93099309
linkType: hard
93109310

9311-
"@metamask/smart-transactions-controller@npm:^22.3.0":
9312-
version: 22.3.0
9313-
resolution: "@metamask/smart-transactions-controller@npm:22.3.0"
9311+
"@metamask/smart-transactions-controller@npm:^22.4.0":
9312+
version: 22.4.0
9313+
resolution: "@metamask/smart-transactions-controller@npm:22.4.0"
93149314
dependencies:
93159315
"@babel/runtime": "npm:^7.24.1"
93169316
"@ethereumjs/tx": "npm:^5.2.1"
@@ -9344,7 +9344,7 @@ __metadata:
93449344
optional: true
93459345
"@metamask/gas-fee-controller":
93469346
optional: true
9347-
checksum: 10/a803add4124e964c7eb39aad5f4aac3e49a4da4eb0a52f1c99509b6edd66fdc72821268c697d511d4635d02ca72fdf3ca1eb7d599a483beb030920c4e9832bf0
9347+
checksum: 10/e1cad73e890c5ce2d6f9422d4ac7bd248ac853d10ecf4d96d5092ff5ae911d18db3a851ca800e492613db44c8b4f8a325543a6fe06c10aab5ae1774c75259455
93489348
languageName: node
93499349
linkType: hard
93509350

@@ -34756,7 +34756,7 @@ __metadata:
3475634756
"@metamask/selected-network-controller": "npm:^25.0.0"
3475734757
"@metamask/signature-controller": "npm:^35.0.0"
3475834758
"@metamask/slip44": "npm:^4.2.0"
34759-
"@metamask/smart-transactions-controller": "npm:^22.3.0"
34759+
"@metamask/smart-transactions-controller": "npm:^22.4.0"
3476034760
"@metamask/snaps-controllers": "npm:^17.2.1"
3476134761
"@metamask/snaps-execution-environments": "npm:^10.4.1"
3476234762
"@metamask/snaps-rpc-methods": "npm:^14.2.0"

0 commit comments

Comments
 (0)