aboutsummaryrefslogtreecommitdiffstats
path: root/app/scripts
diff options
context:
space:
mode:
Diffstat (limited to 'app/scripts')
-rw-r--r--app/scripts/background.js24
-rw-r--r--app/scripts/config.js2
-rw-r--r--app/scripts/contentscript.js6
-rw-r--r--app/scripts/controllers/blacklist.js5
-rw-r--r--app/scripts/controllers/currency.js23
-rw-r--r--app/scripts/controllers/infura.js18
-rw-r--r--app/scripts/controllers/shapeshift.js15
-rw-r--r--app/scripts/controllers/transactions.js60
-rw-r--r--app/scripts/first-time-state.js2
-rw-r--r--app/scripts/inpage.js2
-rw-r--r--app/scripts/lib/getObjStructure.js33
-rw-r--r--app/scripts/lib/migrator/index.js27
-rw-r--r--app/scripts/lib/setupRaven.js2
-rw-r--r--app/scripts/lib/tx-gas-utils.js35
-rw-r--r--app/scripts/lib/tx-state-manager.js16
-rw-r--r--app/scripts/migrations/024.js41
-rw-r--r--app/scripts/migrations/index.js1
17 files changed, 229 insertions, 83 deletions
diff --git a/app/scripts/background.js b/app/scripts/background.js
index 7782fc41e..ec586f642 100644
--- a/app/scripts/background.js
+++ b/app/scripts/background.js
@@ -20,9 +20,10 @@ const reportFailedTxToSentry = require('./lib/reportFailedTxToSentry')
const setupMetamaskMeshMetrics = require('./lib/setupMetamaskMeshMetrics')
const EdgeEncryptor = require('./edge-encryptor')
const getFirstPreferredLangCode = require('./lib/get-first-preferred-lang-code')
+const getObjStructure = require('./lib/getObjStructure')
const STORAGE_KEY = 'metamask-config'
-const METAMASK_DEBUG = 'GULP_METAMASK_DEBUG'
+const METAMASK_DEBUG = process.env.METAMASK_DEBUG
window.log = log
log.setDefaultLevel(METAMASK_DEBUG ? 'debug' : 'warn')
@@ -77,6 +78,16 @@ async function loadStateFromPersistence () {
diskStore.getState() ||
migrator.generateInitialState(firstTimeState)
+ // report migration errors to sentry
+ migrator.on('error', (err) => {
+ // get vault structure without secrets
+ const vaultStructure = getObjStructure(versionedData)
+ raven.captureException(err, {
+ // "extra" key is required by Sentry
+ extra: { vaultStructure },
+ })
+ })
+
// migrate data
versionedData = await migrator.migrateData(versionedData)
if (!versionedData) {
@@ -84,7 +95,14 @@ async function loadStateFromPersistence () {
}
// write to disk
- if (localStore.isSupported) localStore.set(versionedData)
+ if (localStore.isSupported) {
+ localStore.set(versionedData)
+ } else {
+ // throw in setTimeout so as to not block boot
+ setTimeout(() => {
+ throw new Error('MetaMask - Localstore not supported')
+ })
+ }
// return just the data
return versionedData.data
@@ -94,7 +112,7 @@ function setupController (initState, initLangCode) {
//
// MetaMask Controller
//
-
+
const controller = new MetamaskController({
// User confirmation callbacks:
showUnconfirmedMessage: triggerUi,
diff --git a/app/scripts/config.js b/app/scripts/config.js
index 74c5b576e..a8470ed82 100644
--- a/app/scripts/config.js
+++ b/app/scripts/config.js
@@ -13,7 +13,7 @@ const DEFAULT_RPC = 'rinkeby'
const OLD_UI_NETWORK_TYPE = 'network'
const BETA_UI_NETWORK_TYPE = 'networkBeta'
-global.METAMASK_DEBUG = 'GULP_METAMASK_DEBUG'
+global.METAMASK_DEBUG = process.env.METAMASK_DEBUG
module.exports = {
network: {
diff --git a/app/scripts/contentscript.js b/app/scripts/contentscript.js
index 2098fae27..fe1766273 100644
--- a/app/scripts/contentscript.js
+++ b/app/scripts/contentscript.js
@@ -131,7 +131,11 @@ function documentElementCheck () {
}
function blacklistedDomainCheck () {
- var blacklistedDomains = ['uscourts.gov', 'dropbox.com']
+ var blacklistedDomains = [
+ 'uscourts.gov',
+ 'dropbox.com',
+ 'webbyawards.com',
+ ]
var currentUrl = window.location.href
var currentRegex
for (let i = 0; i < blacklistedDomains.length; i++) {
diff --git a/app/scripts/controllers/blacklist.js b/app/scripts/controllers/blacklist.js
index 33c31dab9..df41c90c0 100644
--- a/app/scripts/controllers/blacklist.js
+++ b/app/scripts/controllers/blacklist.js
@@ -41,9 +41,9 @@ class BlacklistController {
scheduleUpdates () {
if (this._phishingUpdateIntervalRef) return
- this.updatePhishingList()
+ this.updatePhishingList().catch(log.warn)
this._phishingUpdateIntervalRef = setInterval(() => {
- this.updatePhishingList()
+ this.updatePhishingList().catch(log.warn)
}, POLLING_INTERVAL)
}
@@ -57,4 +57,3 @@ class BlacklistController {
}
module.exports = BlacklistController
-
diff --git a/app/scripts/controllers/currency.js b/app/scripts/controllers/currency.js
index 930fc52e8..36b8808aa 100644
--- a/app/scripts/controllers/currency.js
+++ b/app/scripts/controllers/currency.js
@@ -43,20 +43,19 @@ class CurrencyController {
this.store.updateState({ conversionDate })
}
- updateConversionRate () {
- const currentCurrency = this.getCurrentCurrency()
- return fetch(`https://api.infura.io/v1/ticker/eth${currentCurrency.toLowerCase()}`)
- .then(response => response.json())
- .then((parsedResponse) => {
+ async updateConversionRate () {
+ let currentCurrency
+ try {
+ currentCurrency = this.getCurrentCurrency()
+ const response = await fetch(`https://api.infura.io/v1/ticker/eth${currentCurrency.toLowerCase()}`)
+ const parsedResponse = await response.json()
this.setConversionRate(Number(parsedResponse.bid))
this.setConversionDate(Number(parsedResponse.timestamp))
- }).catch((err) => {
- if (err) {
- console.warn(`MetaMask - Failed to query currency conversion:`, currentCurrency, err)
- this.setConversionRate(0)
- this.setConversionDate('N/A')
- }
- })
+ } catch (err) {
+ log.warn(`MetaMask - Failed to query currency conversion:`, currentCurrency, err)
+ this.setConversionRate(0)
+ this.setConversionDate('N/A')
+ }
}
scheduleConversionInterval () {
diff --git a/app/scripts/controllers/infura.js b/app/scripts/controllers/infura.js
index 10adb1004..c6b4c9de2 100644
--- a/app/scripts/controllers/infura.js
+++ b/app/scripts/controllers/infura.js
@@ -19,15 +19,13 @@ class InfuraController {
// Responsible for retrieving the status of Infura's nodes. Can return either
// ok, degraded, or down.
- checkInfuraNetworkStatus () {
- return fetch('https://api.infura.io/v1/status/metamask')
- .then(response => response.json())
- .then((parsedResponse) => {
- this.store.updateState({
- infuraNetworkStatus: parsedResponse,
- })
- return parsedResponse
- })
+ async checkInfuraNetworkStatus () {
+ const response = await fetch('https://api.infura.io/v1/status/metamask')
+ const parsedResponse = await response.json()
+ this.store.updateState({
+ infuraNetworkStatus: parsedResponse,
+ })
+ return parsedResponse
}
scheduleInfuraNetworkCheck () {
@@ -35,7 +33,7 @@ class InfuraController {
clearInterval(this.conversionInterval)
}
this.conversionInterval = setInterval(() => {
- this.checkInfuraNetworkStatus()
+ this.checkInfuraNetworkStatus().catch(log.warn)
}, POLLING_INTERVAL)
}
}
diff --git a/app/scripts/controllers/shapeshift.js b/app/scripts/controllers/shapeshift.js
index 3d955c01f..3bbfaa1c5 100644
--- a/app/scripts/controllers/shapeshift.js
+++ b/app/scripts/controllers/shapeshift.js
@@ -45,18 +45,19 @@ class ShapeshiftController {
})
}
- updateTx (tx) {
- const url = `https://shapeshift.io/txStat/${tx.depositAddress}`
- return fetch(url)
- .then((response) => {
- return response.json()
- }).then((json) => {
+ async updateTx (tx) {
+ try {
+ const url = `https://shapeshift.io/txStat/${tx.depositAddress}`
+ const response = await fetch(url)
+ const json = await response.json()
tx.response = json
if (tx.response.status === 'complete') {
tx.time = new Date().getTime()
}
return tx
- })
+ } catch (err) {
+ log.warn(err)
+ }
}
saveTx (tx) {
diff --git a/app/scripts/controllers/transactions.js b/app/scripts/controllers/transactions.js
index 31e53554d..336b0d8f7 100644
--- a/app/scripts/controllers/transactions.js
+++ b/app/scripts/controllers/transactions.js
@@ -185,9 +185,10 @@ module.exports = class TransactionController extends EventEmitter {
async addUnapprovedTransaction (txParams) {
// validate
- await this.txGasUtil.validateTxParams(txParams)
+ const normalizedTxParams = this._normalizeTxParams(txParams)
+ this._validateTxParams(normalizedTxParams)
// construct txMeta
- let txMeta = this.txStateManager.generateTxMeta({txParams})
+ let txMeta = this.txStateManager.generateTxMeta({ txParams: normalizedTxParams })
this.addTx(txMeta)
this.emit('newUnapprovedTx', txMeta)
// add default tx params
@@ -215,7 +216,6 @@ module.exports = class TransactionController extends EventEmitter {
}
txParams.gasPrice = ethUtil.addHexPrefix(gasPrice.toString(16))
txParams.value = txParams.value || '0x0'
- if (txParams.to === null) delete txParams.to
// set gasLimit
return await this.txGasUtil.analyzeGasUsage(txMeta)
}
@@ -314,6 +314,60 @@ module.exports = class TransactionController extends EventEmitter {
// PRIVATE METHODS
//
+ _normalizeTxParams (txParams) {
+ // functions that handle normalizing of that key in txParams
+ const whiteList = {
+ from: from => ethUtil.addHexPrefix(from).toLowerCase(),
+ to: to => ethUtil.addHexPrefix(txParams.to).toLowerCase(),
+ nonce: nonce => ethUtil.addHexPrefix(nonce),
+ value: value => ethUtil.addHexPrefix(value),
+ data: data => ethUtil.addHexPrefix(data),
+ gas: gas => ethUtil.addHexPrefix(gas),
+ gasPrice: gasPrice => ethUtil.addHexPrefix(gasPrice),
+ }
+
+ // apply only keys in the whiteList
+ const normalizedTxParams = {}
+ Object.keys(whiteList).forEach((key) => {
+ if (txParams[key]) normalizedTxParams[key] = whiteList[key](txParams[key])
+ })
+
+ return normalizedTxParams
+ }
+
+ _validateTxParams (txParams) {
+ this._validateFrom(txParams)
+ this._validateRecipient(txParams)
+ if ('value' in txParams) {
+ const value = txParams.value.toString()
+ if (value.includes('-')) {
+ throw new Error(`Invalid transaction value of ${txParams.value} not a positive number.`)
+ }
+
+ if (value.includes('.')) {
+ throw new Error(`Invalid transaction value of ${txParams.value} number must be in wei`)
+ }
+ }
+ }
+
+ _validateFrom (txParams) {
+ if ( !(typeof txParams.from === 'string') ) throw new Error(`Invalid from address ${txParams.from} not a string`)
+ if (!ethUtil.isValidAddress(txParams.from)) throw new Error('Invalid from address')
+ }
+
+ _validateRecipient (txParams) {
+ if (txParams.to === '0x' || txParams.to === null ) {
+ if (txParams.data) {
+ delete txParams.to
+ } else {
+ throw new Error('Invalid recipient address')
+ }
+ } else if ( txParams.to !== undefined && !ethUtil.isValidAddress(txParams.to) ) {
+ throw new Error('Invalid recipient address')
+ }
+ return txParams
+ }
+
_markNonceDuplicatesDropped (txId) {
this.txStateManager.setTxStatusConfirmed(txId)
// get the confirmed transactions nonce and from address
diff --git a/app/scripts/first-time-state.js b/app/scripts/first-time-state.js
index 5e8577100..3063df627 100644
--- a/app/scripts/first-time-state.js
+++ b/app/scripts/first-time-state.js
@@ -1,6 +1,6 @@
// test and development environment variables
const env = process.env.METAMASK_ENV
-const METAMASK_DEBUG = 'GULP_METAMASK_DEBUG'
+const METAMASK_DEBUG = process.env.METAMASK_DEBUG
//
// The default state of MetaMask
diff --git a/app/scripts/inpage.js b/app/scripts/inpage.js
index 9261e7d64..ec99bfc35 100644
--- a/app/scripts/inpage.js
+++ b/app/scripts/inpage.js
@@ -9,7 +9,7 @@ const setupDappAutoReload = require('./lib/auto-reload.js')
const MetamaskInpageProvider = require('./lib/inpage-provider.js')
restoreContextAfterImports()
-const METAMASK_DEBUG = 'GULP_METAMASK_DEBUG'
+const METAMASK_DEBUG = process.env.METAMASK_DEBUG
window.log = log
log.setDefaultLevel(METAMASK_DEBUG ? 'debug' : 'warn')
diff --git a/app/scripts/lib/getObjStructure.js b/app/scripts/lib/getObjStructure.js
new file mode 100644
index 000000000..3db389507
--- /dev/null
+++ b/app/scripts/lib/getObjStructure.js
@@ -0,0 +1,33 @@
+const clone = require('clone')
+
+module.exports = getObjStructure
+
+// This will create an object that represents the structure of the given object
+// it replaces all values with the result of their type
+
+// {
+// "data": {
+// "CurrencyController": {
+// "conversionDate": "number",
+// "conversionRate": "number",
+// "currentCurrency": "string"
+// }
+// }
+
+function getObjStructure(obj) {
+ const structure = clone(obj)
+ return deepMap(structure, (value) => {
+ return value === null ? 'null' : typeof value
+ })
+}
+
+function deepMap(target = {}, visit) {
+ Object.entries(target).forEach(([key, value]) => {
+ if (typeof value === 'object' && value !== null) {
+ target[key] = deepMap(value, visit)
+ } else {
+ target[key] = visit(value)
+ }
+ })
+ return target
+}
diff --git a/app/scripts/lib/migrator/index.js b/app/scripts/lib/migrator/index.js
index 4fd2cae92..85c2717ea 100644
--- a/app/scripts/lib/migrator/index.js
+++ b/app/scripts/lib/migrator/index.js
@@ -1,6 +1,9 @@
-class Migrator {
+const EventEmitter = require('events')
+
+class Migrator extends EventEmitter {
constructor (opts = {}) {
+ super()
const migrations = opts.migrations || []
// sort migrations by version
this.migrations = migrations.sort((a, b) => a.version - b.version)
@@ -12,13 +15,29 @@ class Migrator {
// run all pending migrations on meta in place
async migrateData (versionedData = this.generateInitialState()) {
+ // get all migrations that have not yet been run
const pendingMigrations = this.migrations.filter(migrationIsPending)
+ // perform each migration
for (const index in pendingMigrations) {
const migration = pendingMigrations[index]
- versionedData = await migration.migrate(versionedData)
- if (!versionedData.data) throw new Error('Migrator - migration returned empty data')
- if (versionedData.version !== undefined && versionedData.meta.version !== migration.version) throw new Error('Migrator - Migration did not update version number correctly')
+ try {
+ // attempt migration and validate
+ const migratedData = await migration.migrate(versionedData)
+ if (!migratedData.data) throw new Error('Migrator - migration returned empty data')
+ if (migratedData.version !== undefined && migratedData.meta.version !== migration.version) throw new Error('Migrator - Migration did not update version number correctly')
+ // accept the migration as good
+ versionedData = migratedData
+ } catch (err) {
+ // rewrite error message to add context without clobbering stack
+ const originalErrorMessage = err.message
+ err.message = `MetaMask Migration Error #${migration.version}: ${originalErrorMessage}`
+ console.warn(err.stack)
+ // emit error instead of throw so as to not break the run (gracefully fail)
+ this.emit('error', err)
+ // stop migrating and use state as is
+ return versionedData
+ }
}
return versionedData
diff --git a/app/scripts/lib/setupRaven.js b/app/scripts/lib/setupRaven.js
index b93591e65..9ec9a256f 100644
--- a/app/scripts/lib/setupRaven.js
+++ b/app/scripts/lib/setupRaven.js
@@ -1,5 +1,5 @@
const Raven = require('raven-js')
-const METAMASK_DEBUG = 'GULP_METAMASK_DEBUG'
+const METAMASK_DEBUG = process.env.METAMASK_DEBUG
const extractEthjsErrorMessage = require('./extractEthjsErrorMessage')
const PROD = 'https://3567c198f8a8412082d32655da2961d0@sentry.io/273505'
const DEV = 'https://f59f3dd640d2429d9d0e2445a87ea8e1@sentry.io/273496'
diff --git a/app/scripts/lib/tx-gas-utils.js b/app/scripts/lib/tx-gas-utils.js
index 829b4c421..c579e462a 100644
--- a/app/scripts/lib/tx-gas-utils.js
+++ b/app/scripts/lib/tx-gas-utils.js
@@ -4,7 +4,7 @@ const {
BnMultiplyByFraction,
bnToHex,
} = require('./util')
-const { addHexPrefix, isValidAddress } = require('ethereumjs-util')
+const { addHexPrefix } = require('ethereumjs-util')
const SIMPLE_GAS_COST = '0x5208' // Hex for 21000, cost of a simple send.
/*
@@ -100,37 +100,4 @@ module.exports = class TxGasUtil {
// otherwise use blockGasLimit
return bnToHex(upperGasLimitBn)
}
-
- async validateTxParams (txParams) {
- this.validateFrom(txParams)
- this.validateRecipient(txParams)
- if ('value' in txParams) {
- const value = txParams.value.toString()
- if (value.includes('-')) {
- throw new Error(`Invalid transaction value of ${txParams.value} not a positive number.`)
- }
-
- if (value.includes('.')) {
- throw new Error(`Invalid transaction value of ${txParams.value} number must be in wei`)
- }
- }
- }
-
- validateFrom (txParams) {
- if ( !(typeof txParams.from === 'string') ) throw new Error(`Invalid from address ${txParams.from} not a string`)
- if (!isValidAddress(txParams.from)) throw new Error('Invalid from address')
- }
-
- validateRecipient (txParams) {
- if (txParams.to === '0x' || txParams.to === null ) {
- if (txParams.data) {
- delete txParams.to
- } else {
- throw new Error('Invalid recipient address')
- }
- } else if ( txParams.to !== undefined && !isValidAddress(txParams.to) ) {
- throw new Error('Invalid recipient address')
- }
- return txParams
- }
} \ No newline at end of file
diff --git a/app/scripts/lib/tx-state-manager.js b/app/scripts/lib/tx-state-manager.js
index 23c915a61..d8ea17400 100644
--- a/app/scripts/lib/tx-state-manager.js
+++ b/app/scripts/lib/tx-state-manager.js
@@ -108,6 +108,10 @@ module.exports = class TransactionStateManager extends EventEmitter {
updateTx (txMeta, note) {
// validate txParams
if (txMeta.txParams) {
+ if (typeof txMeta.txParams.data === 'undefined') {
+ delete txMeta.txParams.data
+ }
+
this.validateTxParams(txMeta.txParams)
}
@@ -140,8 +144,16 @@ module.exports = class TransactionStateManager extends EventEmitter {
validateTxParams(txParams) {
Object.keys(txParams).forEach((key) => {
const value = txParams[key]
- if (typeof value !== 'string') throw new Error(`${key}: ${value} in txParams is not a string`)
- if (!ethUtil.isHexPrefixed(value)) throw new Error('is not hex prefixed, everything on txParams must be hex prefixed')
+ // validate types
+ switch (key) {
+ case 'chainId':
+ if (typeof value !== 'number' && typeof value !== 'string') throw new Error(`${key} in txParams is not a Number or hex string. got: (${value})`)
+ break
+ default:
+ if (typeof value !== 'string') throw new Error(`${key} in txParams is not a string. got: (${value})`)
+ if (!ethUtil.isHexPrefixed(value)) throw new Error(`${key} in txParams is not hex prefixed. got: (${value})`)
+ break
+ }
})
}
diff --git a/app/scripts/migrations/024.js b/app/scripts/migrations/024.js
new file mode 100644
index 000000000..d0b276a79
--- /dev/null
+++ b/app/scripts/migrations/024.js
@@ -0,0 +1,41 @@
+
+const version = 24
+
+/*
+
+This migration ensures that the from address in txParams is to lower case for
+all unapproved transactions
+
+*/
+
+const clone = require('clone')
+
+module.exports = {
+ version,
+
+ migrate: async function (originalVersionedData) {
+ const versionedData = clone(originalVersionedData)
+ versionedData.meta.version = version
+ const state = versionedData.data
+ const newState = transformState(state)
+ versionedData.data = newState
+ return versionedData
+ },
+}
+
+function transformState (state) {
+ const newState = state
+ if (!newState.TransactionController) return newState
+ const transactions = newState.TransactionController.transactions
+ newState.TransactionController.transactions = transactions.map((txMeta, _, txList) => {
+ if (
+ txMeta.status === 'unapproved' &&
+ txMeta.txParams &&
+ txMeta.txParams.from
+ ) {
+ txMeta.txParams.from = txMeta.txParams.from.toLowerCase()
+ }
+ return txMeta
+ })
+ return newState
+}
diff --git a/app/scripts/migrations/index.js b/app/scripts/migrations/index.js
index 811e06b6b..7e4542740 100644
--- a/app/scripts/migrations/index.js
+++ b/app/scripts/migrations/index.js
@@ -34,4 +34,5 @@ module.exports = [
require('./021'),
require('./022'),
require('./023'),
+ require('./024'),
]