Skip to content

Commit b450a0b

Browse files
authored
fix: support fallback to sequential batch (MetaMask#17449)
<!-- 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** This PR aims to apply fixes implemented in Transaction controller to v59.0.0. - add support for the scenario when STX is disable for batch transaction it now returns undefined and this is handle in the transaction controller (MetaMask/core#6063) using the fallback to sequential batch. - updates the `getMethodName` utility to normalise the transaction data to lowercase before comparing it against known ABI method selectors. [PR](MetaMask/core#6102) <!-- 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: Added normalization to the `getMethodName` utility to normalise the transaction data to lowercase before comparing it against known ABI method selectors. ## **Related issues** Fixes: MetaMask/MetaMask-planning#5301 and MetaMask/MetaMask-planning#5338 ## **Manual testing steps** 1. Open the MetaMask mobile wallet (Android or iOS). 2. Navigate to the dApp: [https://pocs.neplox.security/metamask-mobile-hack13378158bfadd32/](https://pocs.neplox.security/metamask-mobile-hack13378158bfadd32/) 3. Connect your wallet to the dApp. 4. Trigger the malicious transaction by invoking the `sendTransaction()` function from the page. 5. Observe the transaction preview in MetaMask: - No spending cap warning - Missing token name - Unresolved "to" address (even if it's a well-known address like USDC) - Generic "Approve" label shown without context. ## **Screenshots/Recordings** [spend_cap_2.webm](https://github.com/user-attachments/assets/6ffef452-13ce-4f22-9e71-41dd9f03f781) [sequential_fallback.webm](https://github.com/user-attachments/assets/f6e9c3a3-03ea-4ada-b2a2-9364aaef6544) <!-- 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** - [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 3f9a203 commit b450a0b

3 files changed

Lines changed: 36 additions & 11 deletions

File tree

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

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -221,9 +221,7 @@ function publishBatchSmartTransactionHook({
221221
getSmartTransactionCommonParams(state, transactionMeta.chainId);
222222

223223
if (!shouldUseSmartTransaction) {
224-
throw new Error(
225-
'publishBatchSmartTransactionHook: Smart Transaction is required for batch submissions',
226-
);
224+
return Promise.resolve(undefined);
227225
}
228226

229227
return submitBatchSmartTransactionHook({

app/util/transactions/index.js

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,7 @@ export function generateTransferData(type = undefined, opts = {}) {
275275
* @returns {string | undefined} The four-byte signature if data is provided, otherwise undefined.
276276
*/
277277
export function getFourByteSignature(data) {
278-
return data?.substring(0, 10);
278+
return data?.substring(0, 10)?.toLowerCase();
279279
}
280280

281281
/**
@@ -376,6 +376,15 @@ export function decodeTransferData(type, data) {
376376
}
377377
}
378378

379+
/**
380+
* Normalizes a hexadecimal string to lowercase.
381+
* @param {string} hexString - The hexadecimal string to normalize.
382+
* @returns {string} - The normalized lowercase hexadecimal string.
383+
*/
384+
function normalizeHex(hexString) {
385+
return hexString?.toLowerCase() || '';
386+
}
387+
379388
/**
380389
* @typedef {Object} MethodData
381390
* @property {string} name - The method name
@@ -389,20 +398,30 @@ export function decodeTransferData(type, data) {
389398
*/
390399
export async function getMethodData(data, networkClientId) {
391400
if (data.length < 10) return {};
392-
const fourByteSignature = getFourByteSignature(data);
393-
if (fourByteSignature === TRANSFER_FUNCTION_SIGNATURE) {
401+
402+
const fourByteSignature = normalizeHex(getFourByteSignature(data));
403+
404+
if (fourByteSignature === normalizeHex(TRANSFER_FUNCTION_SIGNATURE)) {
394405
return { name: TOKEN_METHOD_TRANSFER };
395-
} else if (fourByteSignature === TRANSFER_FROM_FUNCTION_SIGNATURE) {
406+
} else if (
407+
fourByteSignature === normalizeHex(TRANSFER_FROM_FUNCTION_SIGNATURE)
408+
) {
396409
return { name: TOKEN_METHOD_TRANSFER_FROM };
397-
} else if (fourByteSignature === APPROVE_FUNCTION_SIGNATURE) {
410+
} else if (fourByteSignature === normalizeHex(APPROVE_FUNCTION_SIGNATURE)) {
398411
return { name: TOKEN_METHOD_APPROVE };
399-
} else if (fourByteSignature === INCREASE_ALLOWANCE_SIGNATURE) {
412+
} else if (fourByteSignature === normalizeHex(INCREASE_ALLOWANCE_SIGNATURE)) {
400413
return { name: TOKEN_METHOD_INCREASE_ALLOWANCE };
401-
} else if (fourByteSignature === SET_APPROVAL_FOR_ALL_SIGNATURE) {
414+
} else if (
415+
fourByteSignature === normalizeHex(SET_APPROVAL_FOR_ALL_SIGNATURE)
416+
) {
402417
return { name: TOKEN_METHOD_SET_APPROVAL_FOR_ALL };
403-
} else if (data.substr(0, 32) === CONTRACT_CREATION_SIGNATURE) {
418+
} else if (
419+
normalizeHex(data.substr(0, 32)) ===
420+
normalizeHex(CONTRACT_CREATION_SIGNATURE)
421+
) {
404422
return { name: CONTRACT_METHOD_DEPLOY };
405423
}
424+
406425
// If it's a new method, use on-chain method registry
407426
try {
408427
const registryObject = await handleMethodData(

app/util/transactions/index.test.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -526,6 +526,14 @@ describe('Transactions utils :: getMethodData', () => {
526526
MOCK_NETWORK_CLIENT_ID,
527527
);
528528
});
529+
530+
it('calls handleMethodData with normalized 4-byte data', async () => {
531+
const transferData =
532+
'0xA9059CBB00000000000000000000000056ced0d816c668d7c0bcc3fbf0ab2c6896f589a';
533+
const result = await getMethodData(transferData, MOCK_NETWORK_CLIENT_ID);
534+
535+
expect(result.name).toStrictEqual(TOKEN_METHOD_TRANSFER);
536+
});
529537
});
530538

531539
describe('Transactions utils :: getActionKey', () => {

0 commit comments

Comments
 (0)