diff --git a/app/scripts/lib/createRPCMethodTrackingMiddleware.test.js b/app/scripts/lib/createRPCMethodTrackingMiddleware.test.js index 2f3f563b3594..41b78c080e0c 100644 --- a/app/scripts/lib/createRPCMethodTrackingMiddleware.test.js +++ b/app/scripts/lib/createRPCMethodTrackingMiddleware.test.js @@ -79,11 +79,16 @@ function getNext(timeout = 500) { const waitForSeconds = async (seconds) => await new Promise((resolve) => setTimeout(resolve, SECOND * seconds)); -jest.mock('@metamask/controller-utils', () => ({ - detectSIWE: jest.fn().mockImplementation(() => { - return { isSIWEMessage: false }; - }), -})); +jest.mock('@metamask/controller-utils', () => { + const actual = jest.requireActual('@metamask/controller-utils'); + + return { + ...actual, + detectSIWE: jest.fn().mockImplementation(() => { + return { isSIWEMessage: false }; + }), + }; +}); describe('createRPCMethodTrackingMiddleware', () => { afterEach(() => { diff --git a/app/scripts/lib/middleware/pending.js b/app/scripts/lib/middleware/pending.js index 9852d55528ed..9e01d11ffcb2 100644 --- a/app/scripts/lib/middleware/pending.js +++ b/app/scripts/lib/middleware/pending.js @@ -13,7 +13,7 @@ export function createPendingNonceMiddleware({ getPendingNonce }) { next(); return; } - res.result = await getPendingNonce(param); + res.result = await getPendingNonce(param, req.networkClientId); }); } diff --git a/app/scripts/lib/transaction/util.test.ts b/app/scripts/lib/transaction/util.test.ts index 7d27624e8cde..1fc5138b0472 100644 --- a/app/scripts/lib/transaction/util.test.ts +++ b/app/scripts/lib/transaction/util.test.ts @@ -139,10 +139,26 @@ describe('Transaction Utils', () => { ).toHaveBeenCalledTimes(1); expect( request.transactionController.addTransaction, - ).toHaveBeenCalledWith( - TRANSACTION_PARAMS_MOCK, - TRANSACTION_OPTIONS_MOCK, - ); + ).toHaveBeenCalledWith(TRANSACTION_PARAMS_MOCK, { + ...TRANSACTION_OPTIONS_MOCK, + }); + }); + + it('adds transaction with networkClientId if process.env.TRANSACTION_MULTICHAIN is set', async () => { + process.env.TRANSACTION_MULTICHAIN = '1'; + + await addTransaction(request); + + expect( + request.transactionController.addTransaction, + ).toHaveBeenCalledTimes(1); + expect( + request.transactionController.addTransaction, + ).toHaveBeenCalledWith(TRANSACTION_PARAMS_MOCK, { + ...TRANSACTION_OPTIONS_MOCK, + networkClientId: 'mockNetworkClientId', + }); + process.env.TRANSACTION_MULTICHAIN = ''; }); it('returns transaction meta', async () => { @@ -418,10 +434,9 @@ describe('Transaction Utils', () => { ).toHaveBeenCalledTimes(1); expect( request.transactionController.addTransaction, - ).toHaveBeenCalledWith( - TRANSACTION_PARAMS_MOCK, - TRANSACTION_OPTIONS_MOCK, - ); + ).toHaveBeenCalledWith(TRANSACTION_PARAMS_MOCK, { + ...TRANSACTION_OPTIONS_MOCK, + }); expect(request.ppomController.usePPOM).toHaveBeenCalledTimes(0); }); @@ -484,10 +499,9 @@ describe('Transaction Utils', () => { ).toHaveBeenCalledTimes(1); expect( request.transactionController.addTransaction, - ).toHaveBeenCalledWith( - TRANSACTION_PARAMS_MOCK, - TRANSACTION_OPTIONS_MOCK, - ); + ).toHaveBeenCalledWith(TRANSACTION_PARAMS_MOCK, { + ...TRANSACTION_OPTIONS_MOCK, + }); expect(request.ppomController.usePPOM).toHaveBeenCalledTimes(0); }); @@ -504,10 +518,9 @@ describe('Transaction Utils', () => { ).toHaveBeenCalledTimes(1); expect( request.transactionController.addTransaction, - ).toHaveBeenCalledWith( - TRANSACTION_PARAMS_MOCK, - TRANSACTION_OPTIONS_MOCK, - ); + ).toHaveBeenCalledWith(TRANSACTION_PARAMS_MOCK, { + ...TRANSACTION_OPTIONS_MOCK, + }); expect(request.ppomController.usePPOM).toHaveBeenCalledTimes(0); }); @@ -533,6 +546,27 @@ describe('Transaction Utils', () => { }); }); + it('adds transaction with networkClientId if process.env.TRANSACTION_MULTICHAIN is set', async () => { + process.env.TRANSACTION_MULTICHAIN = '1'; + + await addDappTransaction(dappRequest); + + expect( + request.transactionController.addTransaction, + ).toHaveBeenCalledTimes(1); + expect( + request.transactionController.addTransaction, + ).toHaveBeenCalledWith(TRANSACTION_PARAMS_MOCK, { + ...TRANSACTION_OPTIONS_MOCK, + networkClientId: 'mockNetworkClientId', + method: DAPP_REQUEST_MOCK.method, + requireApproval: true, + securityAlertResponse: DAPP_REQUEST_MOCK.securityAlertResponse, + type: undefined, + }); + process.env.TRANSACTION_MULTICHAIN = ''; + }); + it('returns transaction hash', async () => { const transactionHash = await addDappTransaction(dappRequest); expect(transactionHash).toStrictEqual(TRANSACTION_META_MOCK.hash); diff --git a/app/scripts/lib/transaction/util.ts b/app/scripts/lib/transaction/util.ts index 5c0516eb7c2e..2e42719f98ea 100644 --- a/app/scripts/lib/transaction/util.ts +++ b/app/scripts/lib/transaction/util.ts @@ -238,13 +238,17 @@ async function addTransactionOrUserOperation( async function addTransactionWithController( request: FinalAddTransactionRequest, ) { - const { transactionController, transactionOptions, transactionParams } = - request; + const { + transactionController, + transactionOptions, + transactionParams, + networkClientId, + } = request; const { result, transactionMeta } = - await transactionController.addTransaction( - transactionParams, - transactionOptions, - ); + await transactionController.addTransaction(transactionParams, { + ...transactionOptions, + ...(process.env.TRANSACTION_MULTICHAIN ? { networkClientId } : {}), + }); return { transactionMeta, diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 4f9d2e67dea1..126ab3ffb981 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -1470,6 +1470,10 @@ export default class MetamaskController extends EventEmitter { getGasFeeEstimates: this.gasFeeController.fetchGasFeeEstimates.bind( this.gasFeeController, ), + getNetworkClientRegistry: + this.networkController.getNetworkClientRegistry.bind( + this.networkController, + ), getNetworkState: () => this.networkController.state, getPermittedAccounts: this.getPermittedAccounts.bind(this), getSavedGasFees: () => @@ -1478,6 +1482,7 @@ export default class MetamaskController extends EventEmitter { ], getSelectedAddress: () => this.accountsController.getSelectedAccount().address, + isMultichainEnabled: process.env.TRANSACTION_MULTICHAIN, incomingTransactions: { includeTokenTransfers: false, isEnabled: () => @@ -1493,7 +1498,12 @@ export default class MetamaskController extends EventEmitter { }, messenger: this.controllerMessenger.getRestricted({ name: 'TransactionController', - allowedActions: [`${this.approvalController.name}:addRequest`], + allowedActions: [ + `${this.approvalController.name}:addRequest`, + 'NetworkController:findNetworkClientIdByChainId', + 'NetworkController:getNetworkClientById', + ], + allowedEvents: [`NetworkController:stateChange`], }), onNetworkStateChange: (listener) => { networkControllerMessenger.subscribe( @@ -4241,7 +4251,9 @@ export default class MetamaskController extends EventEmitter { }) { return { dappRequest, - networkClientId: this.networkController.state.selectedNetworkClientId, + networkClientId: + dappRequest?.networkClientId ?? + this.networkController.state.selectedNetworkClientId, selectedAccount: this.accountsController.getSelectedAccount(), transactionController: this.txController, transactionOptions, @@ -5308,11 +5320,13 @@ export default class MetamaskController extends EventEmitter { * Returns the nonce that will be associated with a transaction once approved * * @param {string} address - The hex string address for the transaction + * @param networkClientId - The optional networkClientId to get the nonce lock with * @returns {Promise} */ - async getPendingNonce(address) { + async getPendingNonce(address, networkClientId) { const { nonceDetails, releaseLock } = await this.txController.getNonceLock( address, + process.env.TRANSACTION_MULTICHAIN ? networkClientId : undefined, ); const pendingNonce = nonceDetails.params.highestSuggested; @@ -5325,10 +5339,14 @@ export default class MetamaskController extends EventEmitter { * Returns the next nonce according to the nonce-tracker * * @param {string} address - The hex string address for the transaction + * @param networkClientId - The optional networkClientId to get the nonce lock with * @returns {Promise} */ - async getNextNonce(address) { - const nonceLock = await this.txController.getNonceLock(address); + async getNextNonce(address, networkClientId) { + const nonceLock = await this.txController.getNonceLock( + address, + process.env.TRANSACTION_MULTICHAIN ? networkClientId : undefined, + ); nonceLock.releaseLock(); return nonceLock.nextNonce; } diff --git a/builds.yml b/builds.yml index 978ae5086e35..3d282460eebd 100644 --- a/builds.yml +++ b/builds.yml @@ -261,6 +261,8 @@ env: - BLOCKAID_PUBLIC_KEY # Determines if feature flagged Multichain UI should be used - MULTICHAIN: '' + # Determines if feature flagged Multichain Transactions should be used + - TRANSACTION_MULTICHAIN: '' ### # Meta variables diff --git a/lavamoat/browserify/beta/policy.json b/lavamoat/browserify/beta/policy.json index 079a449c78c6..c1e2e6710da5 100644 --- a/lavamoat/browserify/beta/policy.json +++ b/lavamoat/browserify/beta/policy.json @@ -47,7 +47,7 @@ "TextEncoder": true }, "packages": { - "@metamask/assets-controllers>multiformats": true + "@ensdomains/content-hash>cids>multibase": true } }, "@ensdomains/content-hash>js-base64": { @@ -1981,6 +1981,7 @@ "@metamask/eth-query": true, "@metamask/gas-fee-controller": true, "@metamask/metamask-eth-abis": true, + "@metamask/network-controller": true, "@metamask/providers>@metamask/rpc-errors": true, "@metamask/transaction-controller>nonce-tracker": true, "@metamask/utils": true, diff --git a/lavamoat/browserify/desktop/policy.json b/lavamoat/browserify/desktop/policy.json index 1fd9c4de9a36..e36bcc712c09 100644 --- a/lavamoat/browserify/desktop/policy.json +++ b/lavamoat/browserify/desktop/policy.json @@ -47,7 +47,7 @@ "TextEncoder": true }, "packages": { - "@metamask/assets-controllers>multiformats": true + "@ensdomains/content-hash>cids>multibase": true } }, "@ensdomains/content-hash>js-base64": { @@ -2234,6 +2234,7 @@ "@metamask/eth-query": true, "@metamask/gas-fee-controller": true, "@metamask/metamask-eth-abis": true, + "@metamask/network-controller": true, "@metamask/providers>@metamask/rpc-errors": true, "@metamask/transaction-controller>nonce-tracker": true, "@metamask/utils": true, diff --git a/lavamoat/browserify/flask/policy.json b/lavamoat/browserify/flask/policy.json index 73cf28268c16..8388ecd2f18b 100644 --- a/lavamoat/browserify/flask/policy.json +++ b/lavamoat/browserify/flask/policy.json @@ -47,7 +47,7 @@ "TextEncoder": true }, "packages": { - "@metamask/assets-controllers>multiformats": true + "@ensdomains/content-hash>cids>multibase": true } }, "@ensdomains/content-hash>js-base64": { @@ -2268,6 +2268,7 @@ "@metamask/eth-query": true, "@metamask/gas-fee-controller": true, "@metamask/metamask-eth-abis": true, + "@metamask/network-controller": true, "@metamask/providers>@metamask/rpc-errors": true, "@metamask/transaction-controller>nonce-tracker": true, "@metamask/utils": true, diff --git a/lavamoat/browserify/main/policy.json b/lavamoat/browserify/main/policy.json index 475459af8e30..8f52bd14b2d7 100644 --- a/lavamoat/browserify/main/policy.json +++ b/lavamoat/browserify/main/policy.json @@ -47,7 +47,7 @@ "TextEncoder": true }, "packages": { - "@metamask/assets-controllers>multiformats": true + "@ensdomains/content-hash>cids>multibase": true } }, "@ensdomains/content-hash>js-base64": { @@ -2191,6 +2191,7 @@ "@metamask/eth-query": true, "@metamask/gas-fee-controller": true, "@metamask/metamask-eth-abis": true, + "@metamask/network-controller": true, "@metamask/providers>@metamask/rpc-errors": true, "@metamask/transaction-controller>nonce-tracker": true, "@metamask/utils": true, diff --git a/lavamoat/browserify/mmi/policy.json b/lavamoat/browserify/mmi/policy.json index 9167168c8b40..6cd1fa3f0130 100644 --- a/lavamoat/browserify/mmi/policy.json +++ b/lavamoat/browserify/mmi/policy.json @@ -47,7 +47,7 @@ "TextEncoder": true }, "packages": { - "@metamask/assets-controllers>multiformats": true + "@ensdomains/content-hash>cids>multibase": true } }, "@ensdomains/content-hash>js-base64": { @@ -2289,6 +2289,7 @@ "@metamask/eth-query": true, "@metamask/gas-fee-controller": true, "@metamask/metamask-eth-abis": true, + "@metamask/network-controller": true, "@metamask/providers>@metamask/rpc-errors": true, "@metamask/transaction-controller>nonce-tracker": true, "@metamask/utils": true, diff --git a/package.json b/package.json index 6d8f589a6a83..261b6b384ead 100644 --- a/package.json +++ b/package.json @@ -225,7 +225,7 @@ "dependencies": { "@babel/runtime": "^7.23.2", "@blockaid/ppom_release": "^1.4.1", - "@ensdomains/content-hash": "^2.5.6", + "@ensdomains/content-hash": "^2.5.7", "@ethereumjs/common": "^3.1.1", "@ethereumjs/tx": "^4.1.1", "@ethersproject/abi": "^5.6.4", @@ -298,7 +298,7 @@ "@metamask/snaps-rpc-methods": "^6.0.0", "@metamask/snaps-sdk": "^2.1.0", "@metamask/snaps-utils": "^6.1.0", - "@metamask/transaction-controller": "^22.0.0", + "@metamask/transaction-controller": "^23.0.0", "@metamask/user-operation-controller": "^1.0.0", "@metamask/utils": "^8.2.1", "@ngraveio/bc-ur": "^1.1.6", diff --git a/ui/store/actions.ts b/ui/store/actions.ts index 7364336a8cd4..560c5cee9b38 100644 --- a/ui/store/actions.ts +++ b/ui/store/actions.ts @@ -57,6 +57,7 @@ import { ///: END:ONLY_INCLUDE_IF getInternalAccountByAddress, getSelectedInternalAccount, + getSelectedNetworkClientId, } from '../selectors'; import { computeEstimatedGasLimit, @@ -4087,10 +4088,12 @@ export function getNextNonce(): ThunkAction< > { return async (dispatch, getState) => { const { address } = getSelectedInternalAccount(getState()); + const networkClientId = getSelectedNetworkClientId(getState()); let nextNonce; try { nextNonce = await submitRequestToBackground('getNextNonce', [ address, + networkClientId, ]); } catch (error) { dispatch(displayWarning(error)); diff --git a/yarn.lock b/yarn.lock index f194eae49234..f9d7ad9d1f14 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1910,7 +1910,7 @@ __metadata: languageName: node linkType: hard -"@ensdomains/content-hash@npm:^2.5.6": +"@ensdomains/content-hash@npm:^2.5.7": version: 2.5.7 resolution: "@ensdomains/content-hash@npm:2.5.7" dependencies: @@ -5415,9 +5415,9 @@ __metadata: languageName: node linkType: hard -"@metamask/transaction-controller@npm:^22.0.0": - version: 22.0.0 - resolution: "@metamask/transaction-controller@npm:22.0.0" +"@metamask/transaction-controller@npm:^23.0.0": + version: 23.0.0 + resolution: "@metamask/transaction-controller@npm:23.0.0" dependencies: "@ethereumjs/common": "npm:^3.2.0" "@ethereumjs/tx": "npm:^4.2.0" @@ -5443,7 +5443,7 @@ __metadata: "@metamask/approval-controller": ^5.1.2 "@metamask/gas-fee-controller": ^13.0.0 "@metamask/network-controller": ^17.2.0 - checksum: 58c212278bcb0abcfb9114f56f5d5167bd1b1a6873811583e327aa89008ff67a9362d1cd0a4481d503b1f81a25495c3c05cbd666502e35e48a45b54d82b740c9 + checksum: a81e9253cfe44f7150550cdb9e67b5d5b51dd383f6d7c026891219cd556b98713ccf4e94ba0b91b8f1fd09ba07efd124e226c8ede21e52cf6e21e76c7c5e37af languageName: node linkType: hard @@ -19595,8 +19595,8 @@ __metadata: linkType: hard "gridplus-sdk@npm:^2.5.1": - version: 2.5.2 - resolution: "gridplus-sdk@npm:2.5.2" + version: 2.5.1 + resolution: "gridplus-sdk@npm:2.5.1" dependencies: "@ethereumjs/common": "npm:3.1.1" "@ethereumjs/tx": "npm:4.1.1" @@ -19617,7 +19617,7 @@ __metadata: rlp: "npm:^3.0.0" secp256k1: "npm:4.0.2" uuid: "npm:^9.0.0" - checksum: 566a9cb7a028cd9f9b650aab3d93c32b88eb4fa7bdf44b506b4cdc85e27dc91dbd3f3297aeed0e8abe8135dc7fbfce6c7d0381d26ab88a51acf4d90195140369 + checksum: 57deeae78fc5f904309e689054baabaed8b078b896ecfd5d724889c6ea424a113db64c3fd79d4dca7cc5f558167d7af754506df5c0692ee76087822ae60c3873 languageName: node linkType: hard @@ -24608,7 +24608,7 @@ __metadata: "@babel/register": "npm:^7.22.15" "@babel/runtime": "npm:^7.23.2" "@blockaid/ppom_release": "npm:^1.4.1" - "@ensdomains/content-hash": "npm:^2.5.6" + "@ensdomains/content-hash": "npm:^2.5.7" "@ethereumjs/common": "npm:^3.1.1" "@ethereumjs/tx": "npm:^4.1.1" "@ethersproject/abi": "npm:^5.6.4" @@ -24694,7 +24694,7 @@ __metadata: "@metamask/snaps-sdk": "npm:^2.1.0" "@metamask/snaps-utils": "npm:^6.1.0" "@metamask/test-dapp": "npm:^7.3.1" - "@metamask/transaction-controller": "npm:^22.0.0" + "@metamask/transaction-controller": "npm:^23.0.0" "@metamask/user-operation-controller": "npm:^1.0.0" "@metamask/utils": "npm:^8.2.1" "@ngraveio/bc-ur": "npm:^1.1.6" @@ -33709,11 +33709,11 @@ __metadata: linkType: hard "uint8arrays@npm:^2.1.3": - version: 2.1.10 - resolution: "uint8arrays@npm:2.1.10" + version: 2.1.5 + resolution: "uint8arrays@npm:2.1.5" dependencies: - multiformats: "npm:^9.4.2" - checksum: 63ceb5fecc09de69641531c847e0b435d15a73587e40d4db23ed9b8a1ebbe839ae39fe81a15ea6079cdf642fcf2583983f9a5d32726edc4bc5e87634f34e3bd5 + multibase: "npm:^4.0.1" + checksum: 521a120ad21250004a95330d0501c87344c5072376b5d5d9ef642721f7913cc1880b823715e5d8829307a9dda73c3064283cb3a7442f0e85fef781cfca4f0334 languageName: node linkType: hard