aboutsummaryrefslogtreecommitdiffstats
path: root/app/scripts/metamask-controller.js
diff options
context:
space:
mode:
Diffstat (limited to 'app/scripts/metamask-controller.js')
-rw-r--r--app/scripts/metamask-controller.js214
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()
}