diff options
Diffstat (limited to 'app/scripts/metamask-controller.js')
-rw-r--r-- | app/scripts/metamask-controller.js | 214 |
1 files changed, 182 insertions, 32 deletions
diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 5ae0f608d..540aee936 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -19,16 +19,16 @@ const createSubscriptionManager = require('eth-json-rpc-filters/subscriptionMana const createOriginMiddleware = require('./lib/createOriginMiddleware') const createLoggerMiddleware = require('./lib/createLoggerMiddleware') const createProviderMiddleware = require('./lib/createProviderMiddleware') -const setupMultiplex = require('./lib/stream-utils.js').setupMultiplex +const {setupMultiplex} = require('./lib/stream-utils.js') const KeyringController = require('eth-keyring-controller') const NetworkController = require('./controllers/network') const PreferencesController = require('./controllers/preferences') const CurrencyController = require('./controllers/currency') const NoticeController = require('./notice-controller') const ShapeShiftController = require('./controllers/shapeshift') -const AddressBookController = require('./controllers/address-book') const InfuraController = require('./controllers/infura') const BlacklistController = require('./controllers/blacklist') +const CachedBalancesController = require('./controllers/cached-balances') const RecentBlocksController = require('./controllers/recent-blocks') const MessageManager = require('./lib/message-manager') const PersonalMessageManager = require('./lib/personal-message-manager') @@ -41,18 +41,21 @@ const ProviderApprovalController = require('./controllers/provider-approval') const nodeify = require('./lib/nodeify') const accountImporter = require('./account-import-strategies') const getBuyEthUrl = require('./lib/buy-eth-url') -const Mutex = require('await-semaphore').Mutex -const version = require('../manifest.json').version -const BN = require('ethereumjs-util').BN +const {Mutex} = require('await-semaphore') +const {version} = require('../manifest.json') +const {BN} = require('ethereumjs-util') const GWEI_BN = new BN('1000000000') const percentile = require('percentile') const seedPhraseVerifier = require('./lib/seed-phrase-verifier') const log = require('loglevel') const TrezorKeyring = require('eth-trezor-keyring') const LedgerBridgeKeyring = require('eth-ledger-bridge-keyring') +const HW_WALLETS_KEYRINGS = [TrezorKeyring.type, LedgerBridgeKeyring.type] const EthQuery = require('eth-query') const ethUtil = require('ethereumjs-util') const sigUtil = require('eth-sig-util') +const { AddressBookController } = require('gaba') + module.exports = class MetamaskController extends EventEmitter { @@ -84,7 +87,7 @@ module.exports = class MetamaskController extends EventEmitter { this.createVaultMutex = new Mutex() // network store - this.networkController = new NetworkController(initState.NetworkController) + this.networkController = new NetworkController(initState.NetworkController, this.platform) // preferences controller this.preferencesController = new PreferencesController({ @@ -117,18 +120,21 @@ module.exports = class MetamaskController extends EventEmitter { // token exchange rate tracker this.tokenRatesController = new TokenRatesController({ + currency: this.currencyController.store, preferences: this.preferencesController.store, }) this.recentBlocksController = new RecentBlocksController({ blockTracker: this.blockTracker, provider: this.provider, + networkController: this.networkController, }) // account tracker watches balances, nonces, and any code at their address. this.accountTracker = new AccountTracker({ provider: this.provider, blockTracker: this.blockTracker, + network: this.networkController, }) // start and stop polling for balances based on activeControllerConnections @@ -140,6 +146,12 @@ module.exports = class MetamaskController extends EventEmitter { } }) + this.cachedBalancesController = new CachedBalancesController({ + accountTracker: this.accountTracker, + getNetwork: this.networkController.getNetworkState.bind(this.networkController), + initState: initState.CachedBalancesController, + }) + // ensure accountTracker updates balances after network change this.networkController.on('networkDidChange', () => { this.accountTracker._updateAccounts() @@ -163,11 +175,7 @@ module.exports = class MetamaskController extends EventEmitter { keyringMemStore: this.keyringController.memStore, }) - // address book controller - this.addressBookController = new AddressBookController({ - initState: initState.AddressBookController, - preferencesStore: this.preferencesController.store, - }) + this.addressBookController = new AddressBookController(undefined, initState.AddressBookController) // tx mgmt this.txController = new TransactionController({ @@ -198,8 +206,8 @@ module.exports = class MetamaskController extends EventEmitter { }) this.networkController.on('networkDidChange', () => { this.balancesController.updateAllBalances() - var currentCurrency = this.currencyController.getCurrentCurrency() - this.setCurrentCurrency(currentCurrency, function() {}) + const currentCurrency = this.currencyController.getCurrentCurrency() + this.setCurrentCurrency(currentCurrency, function () {}) }) this.balancesController.updateAllBalances() @@ -233,12 +241,13 @@ module.exports = class MetamaskController extends EventEmitter { TransactionController: this.txController.store, KeyringController: this.keyringController.store, PreferencesController: this.preferencesController.store, - AddressBookController: this.addressBookController.store, + AddressBookController: this.addressBookController, CurrencyController: this.currencyController.store, NoticeController: this.noticeController.store, ShapeShiftController: this.shapeshiftController.store, NetworkController: this.networkController.store, InfuraController: this.infuraController.store, + CachedBalancesController: this.cachedBalancesController.store, }) this.memStore = new ComposableObservableStore(null, { @@ -246,6 +255,7 @@ module.exports = class MetamaskController extends EventEmitter { AccountTracker: this.accountTracker.store, TxController: this.txController.memStore, BalancesController: this.balancesController.store, + CachedBalancesController: this.cachedBalancesController.store, TokenRatesController: this.tokenRatesController.store, MessageManager: this.messageManager.memStore, PersonalMessageManager: this.personalMessageManager.memStore, @@ -253,7 +263,7 @@ module.exports = class MetamaskController extends EventEmitter { KeyringController: this.keyringController.memStore, PreferencesController: this.preferencesController.store, RecentBlocksController: this.recentBlocksController.store, - AddressBookController: this.addressBookController.store, + AddressBookController: this.addressBookController, CurrencyController: this.currencyController.store, NoticeController: this.noticeController.memStore, ShapeshiftController: this.shapeshiftController.store, @@ -362,7 +372,6 @@ module.exports = class MetamaskController extends EventEmitter { const preferencesController = this.preferencesController const txController = this.txController const noticeController = this.noticeController - const addressBookController = this.addressBookController const networkController = this.networkController const providerApprovalController = this.providerApprovalController @@ -371,6 +380,9 @@ module.exports = class MetamaskController extends EventEmitter { getState: (cb) => cb(null, this.getState()), setCurrentCurrency: this.setCurrentCurrency.bind(this), setUseBlockie: this.setUseBlockie.bind(this), + setParticipateInMetaMetrics: this.setParticipateInMetaMetrics.bind(this), + setMetaMetricsSendCount: this.setMetaMetricsSendCount.bind(this), + setFirstTimeFlowType: this.setFirstTimeFlowType.bind(this), setCurrentLocale: this.setCurrentLocale.bind(this), markAccountsFound: this.markAccountsFound.bind(this), markPasswordForgotten: this.markPasswordForgotten.bind(this), @@ -397,12 +409,16 @@ module.exports = class MetamaskController extends EventEmitter { checkHardwareStatus: nodeify(this.checkHardwareStatus, this), unlockHardwareWalletAccount: nodeify(this.unlockHardwareWalletAccount, this), + // mobile + fetchInfoToSync: nodeify(this.fetchInfoToSync, this), + // vault management submitPassword: nodeify(this.submitPassword, this), // network management setProviderType: nodeify(networkController.setProviderType, networkController), setCustomRpc: nodeify(this.setCustomRpc, this), + updateAndSetCustomRpc: nodeify(this.updateAndSetCustomRpc, this), delCustomRpc: nodeify(this.delCustomRpc, this), // PreferencesController @@ -414,12 +430,15 @@ module.exports = class MetamaskController extends EventEmitter { setAccountLabel: nodeify(preferencesController.setAccountLabel, preferencesController), setFeatureFlag: nodeify(preferencesController.setFeatureFlag, preferencesController), setPreference: nodeify(preferencesController.setPreference, preferencesController), + completeUiMigration: nodeify(preferencesController.completeUiMigration, preferencesController), + completeOnboarding: nodeify(preferencesController.completeOnboarding, preferencesController), + addKnownMethodData: nodeify(preferencesController.addKnownMethodData, preferencesController), // BlacklistController whitelistPhishingDomain: this.whitelistPhishingDomain.bind(this), // AddressController - setAddressBook: nodeify(addressBookController.setAddressBook, addressBookController), + setAddressBook: this.addressBookController.set.bind(this.addressBookController), // KeyringController setLocked: nodeify(this.setLocked, this), @@ -434,6 +453,7 @@ module.exports = class MetamaskController extends EventEmitter { updateAndApproveTransaction: nodeify(txController.updateAndApproveTransaction, txController), retryTransaction: nodeify(this.retryTransaction, this), createCancelTransaction: nodeify(this.createCancelTransaction, this), + createSpeedUpTransaction: nodeify(this.createSpeedUpTransaction, this), getFilteredTxList: nodeify(txController.getFilteredTxList, txController), isNonceTaken: nodeify(txController.isNonceTaken, txController), estimateGas: nodeify(this.estimateGas, this), @@ -568,6 +588,60 @@ module.exports = class MetamaskController extends EventEmitter { }) } + /** + * Collects all the information that we want to share + * with the mobile client for syncing purposes + * @returns Promise<Object> Parts of the state that we want to syncx + */ + async fetchInfoToSync () { + // Preferences + const { + accountTokens, + currentLocale, + frequentRpcList, + identities, + selectedAddress, + tokens, + } = this.preferencesController.store.getState() + + const preferences = { + accountTokens, + currentLocale, + frequentRpcList, + identities, + selectedAddress, + tokens, + } + + // Accounts + const hdKeyring = this.keyringController.getKeyringsByType('HD Key Tree')[0] + const hdAccounts = await hdKeyring.getAccounts() + const accounts = { + hd: hdAccounts.filter((item, pos) => (hdAccounts.indexOf(item) === pos)).map(address => ethUtil.toChecksumAddress(address)), + simpleKeyPair: [], + ledger: [], + trezor: [], + } + + // transactions + + let transactions = this.txController.store.getState().transactions + // delete tx for other accounts that we're not importing + transactions = transactions.filter(tx => { + const checksummedTxFrom = ethUtil.toChecksumAddress(tx.txParams.from) + return ( + accounts.hd.includes(checksummedTxFrom) + ) + }) + + return { + accounts, + preferences, + transactions, + network: this.networkController.store.getState(), + } + } + /* * Submits the user's password and attempts to unlock the vault. * Also synchronizes the preferencesController, to ensure its schema @@ -1024,16 +1098,22 @@ module.exports = class MetamaskController extends EventEmitter { const cleanMsgParams = await this.typedMessageManager.approveMessage(msgParams) const address = sigUtil.normalize(cleanMsgParams.from) const keyring = await this.keyringController.getKeyringForAccount(address) - const wallet = keyring._getWalletForAccount(address) - const privKey = ethUtil.toBuffer(wallet.getPrivateKey()) let signature - switch (version) { - case 'V1': - signature = sigUtil.signTypedDataLegacy(privKey, { data: cleanMsgParams.data }) - break - case 'V3': - signature = sigUtil.signTypedData(privKey, { data: JSON.parse(cleanMsgParams.data) }) - break + // HW Wallet keyrings don't expose private keys + // so we need to handle it separately + if (!HW_WALLETS_KEYRINGS.includes(keyring.type)) { + const wallet = keyring._getWalletForAccount(address) + const privKey = ethUtil.toBuffer(wallet.getPrivateKey()) + switch (version) { + case 'V1': + signature = sigUtil.signTypedDataLegacy(privKey, { data: cleanMsgParams.data }) + break + case 'V3': + signature = sigUtil.signTypedData(privKey, { data: JSON.parse(cleanMsgParams.data) }) + break + } + } else { + signature = await keyring.signTypedData(address, cleanMsgParams.data) } this.typedMessageManager.setMsgStatusSigned(msgId, signature) return this.getState() @@ -1126,8 +1206,8 @@ module.exports = class MetamaskController extends EventEmitter { * @param {string} txId - The ID of the transaction to speed up. * @param {Function} cb - The callback function called with a full state update. */ - async retryTransaction (txId, cb) { - await this.txController.retryTransaction(txId) + async retryTransaction (txId, gasPrice, cb) { + await this.txController.retryTransaction(txId, gasPrice) const state = await this.getState() return state } @@ -1140,7 +1220,17 @@ module.exports = class MetamaskController extends EventEmitter { * @returns {object} MetaMask state */ async createCancelTransaction (originalTxId, customGasPrice, cb) { - await this.txController.createCancelTransaction(originalTxId, customGasPrice) + try { + await this.txController.createCancelTransaction(originalTxId, customGasPrice) + const state = await this.getState() + return state + } catch (error) { + throw error + } + } + + async createSpeedUpTransaction (originalTxId, customGasPrice, cb) { + await this.txController.createSpeedUpTransaction(originalTxId, customGasPrice) const state = await this.getState() return state } @@ -1473,6 +1563,21 @@ module.exports = class MetamaskController extends EventEmitter { } // network + /** + * A method for selecting a custom URL for an ethereum RPC provider and updating it + * @param {string} rpcUrl - A URL for a valid Ethereum RPC API. + * @param {number} chainId - The chainId of the selected network. + * @param {string} ticker - The ticker symbol of the selected network. + * @param {string} nickname - Optional nickname of the selected network. + * @returns {Promise<String>} - The RPC Target URL confirmed. + */ + + async updateAndSetCustomRpc (rpcUrl, chainId, ticker = 'ETH', nickname) { + await this.preferencesController.updateRpc({ rpcUrl, chainId, ticker, nickname }) + this.networkController.setRpcTarget(rpcUrl, chainId, ticker, nickname) + return rpcUrl + } + /** * A method for selecting a custom URL for an ethereum RPC provider. @@ -1483,8 +1588,15 @@ module.exports = class MetamaskController extends EventEmitter { * @returns {Promise<String>} - The RPC Target URL confirmed. */ async setCustomRpc (rpcTarget, chainId, ticker = 'ETH', nickname = '') { - this.networkController.setRpcTarget(rpcTarget, chainId, ticker, nickname) - await this.preferencesController.addToFrequentRpcList(rpcTarget, chainId, ticker, nickname) + const frequentRpcListDetail = this.preferencesController.getFrequentRpcListDetail() + const rpcSettings = frequentRpcListDetail.find((rpc) => rpcTarget === rpc.rpcUrl) + + if (rpcSettings) { + this.networkController.setRpcTarget(rpcSettings.rpcUrl, rpcSettings.chainId, rpcSettings.ticker, rpcSettings.nickname) + } else { + this.networkController.setRpcTarget(rpcTarget, chainId, ticker, nickname) + await this.preferencesController.addToFrequentRpcList(rpcTarget, chainId, ticker, nickname) + } return rpcTarget } @@ -1511,6 +1623,44 @@ module.exports = class MetamaskController extends EventEmitter { } /** + * Sets whether or not the user will have usage data tracked with MetaMetrics + * @param {boolean} bool - True for users that wish to opt-in, false for users that wish to remain out. + * @param {Function} cb - A callback function called when complete. + */ + setParticipateInMetaMetrics (bool, cb) { + try { + const metaMetricsId = this.preferencesController.setParticipateInMetaMetrics(bool) + cb(null, metaMetricsId) + } catch (err) { + cb(err) + } + } + + setMetaMetricsSendCount (val, cb) { + try { + this.preferencesController.setMetaMetricsSendCount(val) + cb(null) + } catch (err) { + cb(err) + } + } + + /** + * Sets the type of first time flow the user wishes to follow: create or import + * @param {String} type - Indicates the type of first time flow the user wishes to follow + * @param {Function} cb - A callback function called when complete. + */ + setFirstTimeFlowType (type, cb) { + try { + this.preferencesController.setFirstTimeFlowType(type) + cb(null) + } catch (err) { + cb(err) + } + } + + + /** * A method for setting a user's current locale, affecting the language rendered. * @param {string} key - Locale identifier. * @param {Function} cb - A callback function called when complete. @@ -1580,7 +1730,7 @@ module.exports = class MetamaskController extends EventEmitter { /** * Locks MetaMask */ - setLocked() { + setLocked () { this.providerApprovalController.setLocked() return this.keyringController.setLocked() } |