diff --git a/app/scripts/controllers/network/network-controller.js b/app/scripts/controllers/network/network-controller.js index 0451b296e..4ab8b5fdd 100644 --- a/app/scripts/controllers/network/network-controller.js +++ b/app/scripts/controllers/network/network-controller.js @@ -27,7 +27,6 @@ import { isSafeChainId, } from '../../../../shared/modules/network.utils'; import getFetchWithTimeout from '../../../../shared/modules/fetch-with-timeout'; -import createMetamaskMiddleware from './createMetamaskMiddleware'; import createInfuraClient from './createInfuraClient'; import createJsonRpcClient from './createJsonRpcClient'; @@ -136,8 +135,7 @@ export default class NetworkController extends EventEmitter { await this._blockTracker.destroy(); } - async initializeProvider(providerParams) { - this._baseProviderParams = providerParams; + async initializeProvider() { const { type, rpcUrl, chainId } = this.getProviderConfig(); this._configureProvider({ type, rpcUrl, chainId }); await this.lookupNetwork(); @@ -470,9 +468,6 @@ export default class NetworkController extends EventEmitter { provider: networkProvider, blockTracker, }); - const metamaskMiddleware = createMetamaskMiddleware( - this._baseProviderParams, - ); const engine = new JsonRpcEngine(); subscriptionManager.events.on('notification', (message) => @@ -480,7 +475,6 @@ export default class NetworkController extends EventEmitter { ); engine.push(filterMiddleware); engine.push(subscriptionManager.middleware); - engine.push(metamaskMiddleware); engine.push(networkMiddleware); const provider = providerFromEngine(engine); diff --git a/app/scripts/controllers/network/network-controller.test.js b/app/scripts/controllers/network/network-controller.test.js index 91bab3fe9..98edf258f 100644 --- a/app/scripts/controllers/network/network-controller.test.js +++ b/app/scripts/controllers/network/network-controller.test.js @@ -1,8 +1,10 @@ import sinon from 'sinon'; import nock from 'nock'; -import { getNetworkDisplayName } from './util'; +import { NETWORK_TO_NAME_MAP } from '../../../../shared/constants/network'; import NetworkController, { NETWORK_EVENTS } from './network-controller'; +const getNetworkDisplayName = (key) => NETWORK_TO_NAME_MAP[key]; + /** * Construct a successful RPC response. * diff --git a/app/scripts/controllers/network/util.js b/app/scripts/controllers/network/util.js deleted file mode 100644 index 21787fa41..000000000 --- a/app/scripts/controllers/network/util.js +++ /dev/null @@ -1,49 +0,0 @@ -import { NETWORK_TO_NAME_MAP } from '../../../../shared/constants/network'; -import { TRANSACTION_ENVELOPE_TYPES } from '../../../../shared/constants/transaction'; - -export const getNetworkDisplayName = (key) => NETWORK_TO_NAME_MAP[key]; - -export function formatTxMetaForRpcResult(txMeta) { - const { r, s, v, hash, txReceipt, txParams } = txMeta; - const { - to, - data, - nonce, - gas, - from, - value, - gasPrice, - accessList, - maxFeePerGas, - maxPriorityFeePerGas, - } = txParams; - - const formattedTxMeta = { - v, - r, - s, - to, - gas, - from, - hash, - nonce, - input: data || '0x', - value: value || '0x0', - accessList: accessList || null, - blockHash: txReceipt?.blockHash || null, - blockNumber: txReceipt?.blockNumber || null, - transactionIndex: txReceipt?.transactionIndex || null, - }; - - if (maxFeePerGas && maxPriorityFeePerGas) { - formattedTxMeta.gasPrice = maxFeePerGas; - formattedTxMeta.maxFeePerGas = maxFeePerGas; - formattedTxMeta.maxPriorityFeePerGas = maxPriorityFeePerGas; - formattedTxMeta.type = TRANSACTION_ENVELOPE_TYPES.FEE_MARKET; - } else { - formattedTxMeta.gasPrice = gasPrice; - formattedTxMeta.type = TRANSACTION_ENVELOPE_TYPES.LEGACY; - } - - return formattedTxMeta; -} diff --git a/app/scripts/controllers/network/util.test.js b/app/scripts/controllers/network/util.test.js deleted file mode 100644 index 49fda6ef8..000000000 --- a/app/scripts/controllers/network/util.test.js +++ /dev/null @@ -1,100 +0,0 @@ -import { - TRANSACTION_STATUSES, - TRANSACTION_TYPES, - TRANSACTION_ENVELOPE_TYPES, -} from '../../../../shared/constants/transaction'; - -import { formatTxMetaForRpcResult } from './util'; - -describe('network utils', () => { - describe('formatTxMetaForRpcResult', () => { - it('should correctly format the tx meta object (EIP-1559)', () => { - const txMeta = { - id: 1, - status: TRANSACTION_STATUSES.UNAPPROVED, - txParams: { - from: '0xc684832530fcbddae4b4230a47e991ddcec2831d', - to: '0x1678a085c290ebd122dc42cba69373b5953b831d', - maxFeePerGas: '0x77359400', - maxPriorityFeePerGas: '0x77359400', - gas: '0x7b0d', - nonce: '0x4b', - }, - type: TRANSACTION_TYPES.SIMPLE_SEND, - origin: 'other', - chainId: '0x5', - time: 1624408066355, - metamaskNetworkId: '5', - hash: '0x4bcb6cd6b182209585f8ad140260ddb35c81a575dd40f508d9767e652a9f60e7', - r: '0x4c3111e42ed5eec3dcecba1e234700f387e8693c373c61c3e54a762a26f1570e', - s: '0x18bfc4eeb7ebcfacc3bd59ea100a6834ea3265e65945dbec69aa2a06564fafff', - v: '0x29', - }; - const expectedResult = { - accessList: null, - blockHash: null, - blockNumber: null, - from: '0xc684832530fcbddae4b4230a47e991ddcec2831d', - gas: '0x7b0d', - gasPrice: '0x77359400', - hash: '0x4bcb6cd6b182209585f8ad140260ddb35c81a575dd40f508d9767e652a9f60e7', - input: '0x', - maxFeePerGas: '0x77359400', - maxPriorityFeePerGas: '0x77359400', - nonce: '0x4b', - r: '0x4c3111e42ed5eec3dcecba1e234700f387e8693c373c61c3e54a762a26f1570e', - s: '0x18bfc4eeb7ebcfacc3bd59ea100a6834ea3265e65945dbec69aa2a06564fafff', - to: '0x1678a085c290ebd122dc42cba69373b5953b831d', - transactionIndex: null, - type: '0x2', - v: '0x29', - value: '0x0', - }; - const result = formatTxMetaForRpcResult(txMeta); - expect(result).toStrictEqual(expectedResult); - }); - - it('should correctly format the tx meta object (non EIP-1559)', () => { - const txMeta = { - id: 1, - status: TRANSACTION_STATUSES.UNAPPROVED, - txParams: { - from: '0xc684832530fcbddae4b4230a47e991ddcec2831d', - to: '0x1678a085c290ebd122dc42cba69373b5953b831d', - gasPrice: '0x77359400', - gas: '0x7b0d', - nonce: '0x4b', - }, - type: TRANSACTION_TYPES.SIMPLE_SEND, - origin: 'other', - chainId: '0x5', - time: 1624408066355, - metamaskNetworkId: '5', - hash: '0x4bcb6cd6b182209585f8ad140260ddb35c81a575dd40f508d9767e652a9f60e7', - r: '0x4c3111e42ed5eec3dcecba1e234700f387e8693c373c61c3e54a762a26f1570e', - s: '0x18bfc4eeb7ebcfacc3bd59ea100a6834ea3265e65945dbec69aa2a06564fafff', - v: '0x29', - }; - const expectedResult = { - accessList: null, - blockHash: null, - blockNumber: null, - from: '0xc684832530fcbddae4b4230a47e991ddcec2831d', - gas: '0x7b0d', - hash: '0x4bcb6cd6b182209585f8ad140260ddb35c81a575dd40f508d9767e652a9f60e7', - input: '0x', - gasPrice: '0x77359400', - nonce: '0x4b', - r: '0x4c3111e42ed5eec3dcecba1e234700f387e8693c373c61c3e54a762a26f1570e', - s: '0x18bfc4eeb7ebcfacc3bd59ea100a6834ea3265e65945dbec69aa2a06564fafff', - to: '0x1678a085c290ebd122dc42cba69373b5953b831d', - transactionIndex: null, - type: TRANSACTION_ENVELOPE_TYPES.LEGACY, - v: '0x29', - value: '0x0', - }; - const result = formatTxMetaForRpcResult(txMeta); - expect(result).toStrictEqual(expectedResult); - }); - }); -}); diff --git a/app/scripts/controllers/network/createMetamaskMiddleware.js b/app/scripts/lib/createMetamaskMiddleware.js similarity index 100% rename from app/scripts/controllers/network/createMetamaskMiddleware.js rename to app/scripts/lib/createMetamaskMiddleware.js diff --git a/app/scripts/controllers/network/middleware/pending.js b/app/scripts/lib/middleware/pending.js similarity index 100% rename from app/scripts/controllers/network/middleware/pending.js rename to app/scripts/lib/middleware/pending.js diff --git a/app/scripts/controllers/network/pending-middleware.test.js b/app/scripts/lib/middleware/pending.test.js similarity index 99% rename from app/scripts/controllers/network/pending-middleware.test.js rename to app/scripts/lib/middleware/pending.test.js index 5b34a48c3..d3068bf54 100644 --- a/app/scripts/controllers/network/pending-middleware.test.js +++ b/app/scripts/lib/middleware/pending.test.js @@ -4,7 +4,7 @@ import { txMetaStub } from '../../../../test/stub/tx-meta-stub'; import { createPendingNonceMiddleware, createPendingTxMiddleware, -} from './middleware/pending'; +} from './pending'; describe('PendingNonceMiddleware', () => { describe('#createPendingNonceMiddleware', () => { diff --git a/app/scripts/lib/util.js b/app/scripts/lib/util.js index bfd7469d4..f225ddad7 100644 --- a/app/scripts/lib/util.js +++ b/app/scripts/lib/util.js @@ -15,6 +15,7 @@ import { PLATFORM_BRAVE, } from '../../../shared/constants/app'; import { stripHexPrefix } from '../../../shared/modules/hexstring-utils'; +import { TRANSACTION_ENVELOPE_TYPES } from '../../../shared/constants/transaction'; /** * @see {@link getEnvironmentType} @@ -253,3 +254,48 @@ export function addUrlProtocolPrefix(urlString) { } return urlString; } + +export function formatTxMetaForRpcResult(txMeta) { + const { r, s, v, hash, txReceipt, txParams } = txMeta; + const { + to, + data, + nonce, + gas, + from, + value, + gasPrice, + accessList, + maxFeePerGas, + maxPriorityFeePerGas, + } = txParams; + + const formattedTxMeta = { + v, + r, + s, + to, + gas, + from, + hash, + nonce, + input: data || '0x', + value: value || '0x0', + accessList: accessList || null, + blockHash: txReceipt?.blockHash || null, + blockNumber: txReceipt?.blockNumber || null, + transactionIndex: txReceipt?.transactionIndex || null, + }; + + if (maxFeePerGas && maxPriorityFeePerGas) { + formattedTxMeta.gasPrice = maxFeePerGas; + formattedTxMeta.maxFeePerGas = maxFeePerGas; + formattedTxMeta.maxPriorityFeePerGas = maxPriorityFeePerGas; + formattedTxMeta.type = TRANSACTION_ENVELOPE_TYPES.FEE_MARKET; + } else { + formattedTxMeta.gasPrice = gasPrice; + formattedTxMeta.type = TRANSACTION_ENVELOPE_TYPES.LEGACY; + } + + return formattedTxMeta; +} diff --git a/app/scripts/lib/util.test.js b/app/scripts/lib/util.test.js index a4c5d7b5f..7faf3ac4d 100644 --- a/app/scripts/lib/util.test.js +++ b/app/scripts/lib/util.test.js @@ -9,7 +9,17 @@ import { PLATFORM_CHROME, PLATFORM_EDGE, } from '../../../shared/constants/app'; -import { deferredPromise, getEnvironmentType, getPlatform } from './util'; +import { + TRANSACTION_STATUSES, + TRANSACTION_TYPES, + TRANSACTION_ENVELOPE_TYPES, +} from '../../../shared/constants/transaction'; +import { + deferredPromise, + getEnvironmentType, + getPlatform, + formatTxMetaForRpcResult, +} from './util'; describe('app utils', () => { describe('getEnvironmentType', () => { @@ -208,4 +218,95 @@ describe('app utils', () => { await expect(promise).resolves.toBe('test'); }); }); + + describe('formatTxMetaForRpcResult', () => { + it('should correctly format the tx meta object (EIP-1559)', () => { + const txMeta = { + id: 1, + status: TRANSACTION_STATUSES.UNAPPROVED, + txParams: { + from: '0xc684832530fcbddae4b4230a47e991ddcec2831d', + to: '0x1678a085c290ebd122dc42cba69373b5953b831d', + maxFeePerGas: '0x77359400', + maxPriorityFeePerGas: '0x77359400', + gas: '0x7b0d', + nonce: '0x4b', + }, + type: TRANSACTION_TYPES.SIMPLE_SEND, + origin: 'other', + chainId: '0x5', + time: 1624408066355, + metamaskNetworkId: '5', + hash: '0x4bcb6cd6b182209585f8ad140260ddb35c81a575dd40f508d9767e652a9f60e7', + r: '0x4c3111e42ed5eec3dcecba1e234700f387e8693c373c61c3e54a762a26f1570e', + s: '0x18bfc4eeb7ebcfacc3bd59ea100a6834ea3265e65945dbec69aa2a06564fafff', + v: '0x29', + }; + const expectedResult = { + accessList: null, + blockHash: null, + blockNumber: null, + from: '0xc684832530fcbddae4b4230a47e991ddcec2831d', + gas: '0x7b0d', + gasPrice: '0x77359400', + hash: '0x4bcb6cd6b182209585f8ad140260ddb35c81a575dd40f508d9767e652a9f60e7', + input: '0x', + maxFeePerGas: '0x77359400', + maxPriorityFeePerGas: '0x77359400', + nonce: '0x4b', + r: '0x4c3111e42ed5eec3dcecba1e234700f387e8693c373c61c3e54a762a26f1570e', + s: '0x18bfc4eeb7ebcfacc3bd59ea100a6834ea3265e65945dbec69aa2a06564fafff', + to: '0x1678a085c290ebd122dc42cba69373b5953b831d', + transactionIndex: null, + type: '0x2', + v: '0x29', + value: '0x0', + }; + const result = formatTxMetaForRpcResult(txMeta); + expect(result).toStrictEqual(expectedResult); + }); + + it('should correctly format the tx meta object (non EIP-1559)', () => { + const txMeta = { + id: 1, + status: TRANSACTION_STATUSES.UNAPPROVED, + txParams: { + from: '0xc684832530fcbddae4b4230a47e991ddcec2831d', + to: '0x1678a085c290ebd122dc42cba69373b5953b831d', + gasPrice: '0x77359400', + gas: '0x7b0d', + nonce: '0x4b', + }, + type: TRANSACTION_TYPES.SIMPLE_SEND, + origin: 'other', + chainId: '0x5', + time: 1624408066355, + metamaskNetworkId: '5', + hash: '0x4bcb6cd6b182209585f8ad140260ddb35c81a575dd40f508d9767e652a9f60e7', + r: '0x4c3111e42ed5eec3dcecba1e234700f387e8693c373c61c3e54a762a26f1570e', + s: '0x18bfc4eeb7ebcfacc3bd59ea100a6834ea3265e65945dbec69aa2a06564fafff', + v: '0x29', + }; + const expectedResult = { + accessList: null, + blockHash: null, + blockNumber: null, + from: '0xc684832530fcbddae4b4230a47e991ddcec2831d', + gas: '0x7b0d', + hash: '0x4bcb6cd6b182209585f8ad140260ddb35c81a575dd40f508d9767e652a9f60e7', + input: '0x', + gasPrice: '0x77359400', + nonce: '0x4b', + r: '0x4c3111e42ed5eec3dcecba1e234700f387e8693c373c61c3e54a762a26f1570e', + s: '0x18bfc4eeb7ebcfacc3bd59ea100a6834ea3265e65945dbec69aa2a06564fafff', + to: '0x1678a085c290ebd122dc42cba69373b5953b831d', + transactionIndex: null, + type: TRANSACTION_ENVELOPE_TYPES.LEGACY, + v: '0x29', + value: '0x0', + }; + const result = formatTxMetaForRpcResult(txMeta); + expect(result).toStrictEqual(expectedResult); + }); + }); }); diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 3a9563b92..c4277d576 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -146,6 +146,7 @@ import MetaMetricsController from './controllers/metametrics'; import { segment } from './lib/segment'; import createMetaRPCHandler from './lib/createMetaRPCHandler'; import { previousValueComparator } from './lib/util'; +import createMetamaskMiddleware from './lib/createMetamaskMiddleware'; import { CaveatMutatorFactories, @@ -246,9 +247,7 @@ export default class MetamaskController extends EventEmitter { state: initState.NetworkController, infuraProjectId: opts.infuraProjectId, }); - - // now we can initialize the RPC provider, which other controllers require - this.initializeProvider(); + this.networkController.initializeProvider(); this.provider = this.networkController.getProviderAndBlockTracker().provider; this.blockTracker = @@ -1050,6 +1049,48 @@ export default class MetamaskController extends EventEmitter { this.messageManager.clearUnapproved(); }); + this.metamaskMiddleware = createMetamaskMiddleware({ + static: { + eth_syncing: false, + web3_clientVersion: `MetaMask/v${version}`, + }, + version, + // account mgmt + getAccounts: async ( + { origin: innerOrigin }, + { suppressUnauthorizedError = true } = {}, + ) => { + if (innerOrigin === ORIGIN_METAMASK) { + const selectedAddress = + this.preferencesController.getSelectedAddress(); + return selectedAddress ? [selectedAddress] : []; + } else if (this.isUnlocked()) { + return await this.getPermittedAccounts(innerOrigin, { + suppressUnauthorizedError, + }); + } + return []; // changing this is a breaking change + }, + // tx signing + processTransaction: this.newUnapprovedTransaction.bind(this), + // msg signing + processEthSignMessage: this.newUnsignedMessage.bind(this), + processTypedMessage: this.newUnsignedTypedMessage.bind(this), + processTypedMessageV3: this.newUnsignedTypedMessage.bind(this), + processTypedMessageV4: this.newUnsignedTypedMessage.bind(this), + processPersonalMessage: this.newUnsignedPersonalMessage.bind(this), + processDecryptMessage: this.newRequestDecryptMessage.bind(this), + processEncryptionPublicKey: this.newRequestEncryptionPublicKey.bind(this), + getPendingNonce: this.getPendingNonce.bind(this), + getPendingTransactionByHash: (hash) => + this.txController.getTransactions({ + searchCriteria: { + hash, + status: TRANSACTION_STATUSES.SUBMITTED, + }, + })[0], + }); + // ensure isClientOpenAndUnlocked is updated when memState updates this.on('update', (memState) => this._onStateUpdate(memState)); @@ -1442,57 +1483,6 @@ export default class MetamaskController extends EventEmitter { ///: END:ONLY_INCLUDE_IN } - /** - * Constructor helper: initialize a provider. - */ - initializeProvider() { - const version = this.platform.getVersion(); - const providerOpts = { - static: { - eth_syncing: false, - web3_clientVersion: `MetaMask/v${version}`, - }, - version, - // account mgmt - getAccounts: async ( - { origin }, - { suppressUnauthorizedError = true } = {}, - ) => { - if (origin === ORIGIN_METAMASK) { - const selectedAddress = - this.preferencesController.getSelectedAddress(); - return selectedAddress ? [selectedAddress] : []; - } else if (this.isUnlocked()) { - return await this.getPermittedAccounts(origin, { - suppressUnauthorizedError, - }); - } - return []; // changing this is a breaking change - }, - // tx signing - processTransaction: this.newUnapprovedTransaction.bind(this), - // msg signing - processEthSignMessage: this.newUnsignedMessage.bind(this), - processTypedMessage: this.newUnsignedTypedMessage.bind(this), - processTypedMessageV3: this.newUnsignedTypedMessage.bind(this), - processTypedMessageV4: this.newUnsignedTypedMessage.bind(this), - processPersonalMessage: this.newUnsignedPersonalMessage.bind(this), - processDecryptMessage: this.newRequestDecryptMessage.bind(this), - processEncryptionPublicKey: this.newRequestEncryptionPublicKey.bind(this), - getPendingNonce: this.getPendingNonce.bind(this), - getPendingTransactionByHash: (hash) => - this.txController.getTransactions({ - searchCriteria: { - hash, - status: TRANSACTION_STATUSES.SUBMITTED, - }, - })[0], - }; - const providerProxy = - this.networkController.initializeProvider(providerOpts); - return providerProxy; - } - /** * TODO:LegacyProvider: Delete * Constructor helper: initialize a public config store. @@ -3919,6 +3909,8 @@ export default class MetamaskController extends EventEmitter { ); } + engine.push(this.metamaskMiddleware); + // forward to metamask primary provider engine.push(providerAsMiddleware(provider)); return engine; diff --git a/app/scripts/metamask-controller.test.js b/app/scripts/metamask-controller.test.js index daeabcd0e..bfdd69c6b 100644 --- a/app/scripts/metamask-controller.test.js +++ b/app/scripts/metamask-controller.test.js @@ -190,21 +190,6 @@ describe('MetaMaskController', function () { }); }); - describe('#getAccounts', function () { - it('returns first address when dapp calls web3.eth.getAccounts', async function () { - const password = 'a-fake-password'; - await metamaskController.createNewVaultAndRestore(password, TEST_SEED); - - metamaskController.networkController._baseProviderParams.getAccounts( - (err, res) => { - assert.ifError(err); - assert.equal(res.length, 1); - assert.equal(res[0], '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc'); - }, - ); - }); - }); - describe('#importAccountWithStrategy', function () { const importPrivkey = '4cfd3e90fc78b0f86bf7524722150bb8da9c60cd532564d7ff43f5716514f553';