From 1749fcb58d52d8fb626af6936b66ea0333b1dd67 Mon Sep 17 00:00:00 2001 From: dan437 <80175477+dan437@users.noreply.github.com> Date: Mon, 14 Jul 2025 14:00:41 +0200 Subject: [PATCH 1/4] Use the parse function from @ethersproject/transactions Signed-off-by: dan437 <80175477+dan437@users.noreply.github.com> --- package.json | 1 + src/utils.ts | 10 +++------- yarn.lock | 1 + 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index 10f2e72b..debd83ad 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "@ethereumjs/tx": "^5.2.1", "@ethereumjs/util": "^9.0.2", "@ethersproject/bytes": "^5.7.0", + "@ethersproject/transactions": "^5.7.0", "@metamask/base-controller": "^7.0.1", "@metamask/controller-utils": "^11.0.0", "@metamask/eth-json-rpc-provider": "^4.1.6", diff --git a/src/utils.ts b/src/utils.ts index 1fd34882..f368cbf6 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,6 +1,5 @@ -import { TransactionFactory } from '@ethereumjs/tx'; -import { bytesToHex } from '@ethereumjs/util'; import { hexlify } from '@ethersproject/bytes'; +import { parse } from '@ethersproject/transactions'; import type { TransactionMeta } from '@metamask/transaction-controller'; import { TransactionStatus } from '@metamask/transaction-controller'; import { BigNumber } from 'bignumber.js'; @@ -228,11 +227,8 @@ export const getTxHash = (signedTxHex: any) => { if (!signedTxHex) { return ''; } - const txHashBytes = TransactionFactory.fromSerializedData( - // eslint-disable-next-line no-restricted-globals - Buffer.from(signedTxHex.slice(2), 'hex'), - ).hash(); - return bytesToHex(txHashBytes); + const parsed = parse(signedTxHex); + return parsed?.hash ?? ''; }; export const getSmartTransactionMetricsProperties = ( diff --git a/yarn.lock b/yarn.lock index 5ecc57c3..f12ef88b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1660,6 +1660,7 @@ __metadata: "@ethereumjs/tx": ^5.2.1 "@ethereumjs/util": ^9.0.2 "@ethersproject/bytes": ^5.7.0 + "@ethersproject/transactions": ^5.7.0 "@lavamoat/allow-scripts": ^3.2.1 "@lavamoat/preinstall-always-fail": ^2.1.0 "@metamask/auto-changelog": ^3.1.0 From 4322f532c37543cda429c20d063c9c58701281c6 Mon Sep 17 00:00:00 2001 From: dan437 <80175477+dan437@users.noreply.github.com> Date: Mon, 14 Jul 2025 14:34:38 +0200 Subject: [PATCH 2/4] Update tests Signed-off-by: dan437 <80175477+dan437@users.noreply.github.com> --- src/SmartTransactionsController.test.ts | 11 +++-------- src/utils.test.ts | 2 +- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/src/SmartTransactionsController.test.ts b/src/SmartTransactionsController.test.ts index b0e2bf91..1693560e 100644 --- a/src/SmartTransactionsController.test.ts +++ b/src/SmartTransactionsController.test.ts @@ -39,11 +39,6 @@ import type { SmartTransaction, UnsignedTransaction, Hex } from './types'; import { SmartTransactionStatuses, ClientId } from './types'; import * as utils from './utils'; -jest.mock('@ethersproject/bytes', () => ({ - ...jest.requireActual('@ethersproject/bytes'), - hexlify: (str: string) => `0x${str}`, -})); - jest.mock('@metamask/eth-query', () => { const EthQuery = jest.requireActual('@metamask/eth-query'); return class FakeEthQuery extends EthQuery { @@ -730,7 +725,7 @@ describe('SmartTransactionsController', () => { it('should acquire nonce for Swap transactions only', async () => { // Create a mock for getNonceLock const mockGetNonceLock = jest.fn().mockResolvedValue({ - nextNonce: 'nextNonce', + nextNonce: 42, nonceDetails: { test: 'details' }, releaseLock: jest.fn(), }); @@ -840,7 +835,7 @@ describe('SmartTransactionsController', () => { ][0]; // Verify nonce was set correctly on the txParams in the created transaction - expect(createdSmartTransaction.txParams.nonce).toBe('0x42'); // 42 as a hex string + expect(createdSmartTransaction.txParams.nonce).toBe('0x2a'); // 42 in hex // Verify transaction type is set to 'swap' by default expect(createdSmartTransaction.type).toBe('swap'); @@ -2652,7 +2647,7 @@ async function withController( messenger, clientId: ClientId.Mobile, getNonceLock: jest.fn().mockResolvedValue({ - nextNonce: 'nextNonce', + nextNonce: 42, releaseLock: jest.fn(), }), confirmExternalTransaction: jest.fn(), diff --git a/src/utils.test.ts b/src/utils.test.ts index 5d06b53f..b6f88a9d 100644 --- a/src/utils.test.ts +++ b/src/utils.test.ts @@ -372,7 +372,7 @@ describe('src/utils.js', () => { it('throws an error with an incorrect signed transaction', () => { expect(() => { utils.getTxHash('0x0302b75dfb9fd9eb34056af0'); - }).toThrow('kzg instance required to instantiate blob tx'); + }).toThrow('unsupported transaction type: 3'); }); }); From b35b826fa35594f08e98c2886a6e0b9771ff9a9e Mon Sep 17 00:00:00 2001 From: dan437 <80175477+dan437@users.noreply.github.com> Date: Tue, 15 Jul 2025 16:25:51 +0200 Subject: [PATCH 3/4] Parse type 4 transactions --- package.json | 1 + src/utils.ts | 14 +++++++++++--- yarn.lock | 27 +++++++++++++++++++++++++++ 3 files changed, 39 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index debd83ad..38fc1fee 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,7 @@ "lodash": "^4.17.21" }, "devDependencies": { + "@ethersproject/keccak256": "^5.8.0", "@lavamoat/allow-scripts": "^3.2.1", "@lavamoat/preinstall-always-fail": "^2.1.0", "@metamask/auto-changelog": "^3.1.0", diff --git a/src/utils.ts b/src/utils.ts index f368cbf6..b223183d 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,4 +1,5 @@ -import { hexlify } from '@ethersproject/bytes'; +import { arrayify, hexlify } from '@ethersproject/bytes'; +import { keccak256 } from '@ethersproject/keccak256'; import { parse } from '@ethersproject/transactions'; import type { TransactionMeta } from '@metamask/transaction-controller'; import { TransactionStatus } from '@metamask/transaction-controller'; @@ -227,8 +228,15 @@ export const getTxHash = (signedTxHex: any) => { if (!signedTxHex) { return ''; } - const parsed = parse(signedTxHex); - return parsed?.hash ?? ''; + try { + const parsed = parse(signedTxHex); + return parsed?.hash ?? ''; + } catch (error) { + if (typeof signedTxHex === 'string' && signedTxHex.startsWith('0x04')) { + return hexlify(keccak256(arrayify(signedTxHex))); + } + throw error; + } }; export const getSmartTransactionMetricsProperties = ( diff --git a/yarn.lock b/yarn.lock index f12ef88b..d24e59ed 100644 --- a/yarn.lock +++ b/yarn.lock @@ -665,6 +665,15 @@ __metadata: languageName: node linkType: hard +"@ethersproject/bytes@npm:^5.8.0": + version: 5.8.0 + resolution: "@ethersproject/bytes@npm:5.8.0" + dependencies: + "@ethersproject/logger": ^5.8.0 + checksum: 507e8ef1f1559590b4e78e3392a2b16090e96fb1091e0b08d3a8491df65976b313c29cdb412594454f68f9f04d5f77ea5a400b489d80a3e46a608156ef31b251 + languageName: node + linkType: hard + "@ethersproject/constants@npm:^5.7.0": version: 5.7.0 resolution: "@ethersproject/constants@npm:5.7.0" @@ -719,6 +728,16 @@ __metadata: languageName: node linkType: hard +"@ethersproject/keccak256@npm:^5.8.0": + version: 5.8.0 + resolution: "@ethersproject/keccak256@npm:5.8.0" + dependencies: + "@ethersproject/bytes": ^5.8.0 + js-sha3: 0.8.0 + checksum: af3621d2b18af6c8f5181dacad91e1f6da4e8a6065668b20e4c24684bdb130b31e45e0d4dbaed86d4f1314d01358aa119f05be541b696e455424c47849d81913 + languageName: node + linkType: hard + "@ethersproject/logger@npm:^5.7.0": version: 5.7.0 resolution: "@ethersproject/logger@npm:5.7.0" @@ -726,6 +745,13 @@ __metadata: languageName: node linkType: hard +"@ethersproject/logger@npm:^5.8.0": + version: 5.8.0 + resolution: "@ethersproject/logger@npm:5.8.0" + checksum: 6249885a7fd4a5806e4c8700b76ffcc8f1ff00d71f31aa717716a89fa6b391de19fbb0cb5ae2560b9f57ec0c2e8e0a11ebc2099124c73d3b42bc58e3eedc41d1 + languageName: node + linkType: hard + "@ethersproject/networks@npm:^5.7.0": version: 5.7.1 resolution: "@ethersproject/networks@npm:5.7.1" @@ -1660,6 +1686,7 @@ __metadata: "@ethereumjs/tx": ^5.2.1 "@ethereumjs/util": ^9.0.2 "@ethersproject/bytes": ^5.7.0 + "@ethersproject/keccak256": ^5.8.0 "@ethersproject/transactions": ^5.7.0 "@lavamoat/allow-scripts": ^3.2.1 "@lavamoat/preinstall-always-fail": ^2.1.0 From 566a02df0cff5b539b6cb124ba36bd34e7bf8fcc Mon Sep 17 00:00:00 2001 From: dan437 <80175477+dan437@users.noreply.github.com> Date: Tue, 15 Jul 2025 16:37:31 +0200 Subject: [PATCH 4/4] Add a test for type 4 transaction, refactoring Signed-off-by: dan437 <80175477+dan437@users.noreply.github.com> Update deps Signed-off-by: dan437 <80175477+dan437@users.noreply.github.com> Update Signed-off-by: dan437 <80175477+dan437@users.noreply.github.com> --- package.json | 2 +- src/utils.test.ts | 9 +++++++++ src/utils.ts | 6 +++++- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 38fc1fee..ade7ce8d 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "@ethereumjs/tx": "^5.2.1", "@ethereumjs/util": "^9.0.2", "@ethersproject/bytes": "^5.7.0", + "@ethersproject/keccak256": "^5.8.0", "@ethersproject/transactions": "^5.7.0", "@metamask/base-controller": "^7.0.1", "@metamask/controller-utils": "^11.0.0", @@ -41,7 +42,6 @@ "lodash": "^4.17.21" }, "devDependencies": { - "@ethersproject/keccak256": "^5.8.0", "@lavamoat/allow-scripts": "^3.2.1", "@lavamoat/preinstall-always-fail": "^2.1.0", "@metamask/auto-changelog": "^3.1.0", diff --git a/src/utils.test.ts b/src/utils.test.ts index b6f88a9d..42a8aa46 100644 --- a/src/utils.test.ts +++ b/src/utils.test.ts @@ -1,3 +1,5 @@ +import { arrayify, hexlify } from '@ethersproject/bytes'; +import { keccak256 } from '@ethersproject/keccak256'; import { ChainId, NetworkType } from '@metamask/controller-utils'; import { type TransactionMeta, @@ -374,6 +376,13 @@ describe('src/utils.js', () => { utils.getTxHash('0x0302b75dfb9fd9eb34056af0'); }).toThrow('unsupported transaction type: 3'); }); + + it('computes hash for type 4 transaction', () => { + const type4TxHex = '0x04010203040506070809'; + const expectedHash = hexlify(keccak256(arrayify(type4TxHex))); + const txHash = utils.getTxHash(type4TxHex); + expect(txHash).toBe(expectedHash); + }); }); describe('getReturnTxHashAsap', () => { diff --git a/src/utils.ts b/src/utils.ts index b223183d..cabdc178 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -224,6 +224,10 @@ export const incrementNonceInHex = (nonceInHex: string): string => { return hexlify(Number(nonceInDec) + 1); }; +const isType4Transaction = (signedTxHex: string) => { + return typeof signedTxHex === 'string' && signedTxHex.startsWith('0x04'); +}; + export const getTxHash = (signedTxHex: any) => { if (!signedTxHex) { return ''; @@ -232,7 +236,7 @@ export const getTxHash = (signedTxHex: any) => { const parsed = parse(signedTxHex); return parsed?.hash ?? ''; } catch (error) { - if (typeof signedTxHex === 'string' && signedTxHex.startsWith('0x04')) { + if (isType4Transaction(signedTxHex)) { return hexlify(keccak256(arrayify(signedTxHex))); } throw error;