diff options
Diffstat (limited to 'app/scripts/lib')
-rw-r--r-- | app/scripts/lib/auto-faucet.js | 20 | ||||
-rw-r--r-- | app/scripts/lib/auto-reload.js | 47 | ||||
-rw-r--r-- | app/scripts/lib/buy-eth-url.js | 23 | ||||
-rw-r--r-- | app/scripts/lib/config-manager.js | 80 | ||||
-rw-r--r-- | app/scripts/lib/eth-store.js | 10 | ||||
-rw-r--r-- | app/scripts/lib/extension-instance.js | 68 | ||||
-rw-r--r-- | app/scripts/lib/extension.js | 14 | ||||
-rw-r--r-- | app/scripts/lib/hex-to-bn.js | 7 | ||||
-rw-r--r-- | app/scripts/lib/id-management.js | 90 | ||||
-rw-r--r-- | app/scripts/lib/idStore-migrator.js | 80 | ||||
-rw-r--r-- | app/scripts/lib/idStore.js | 343 | ||||
-rw-r--r-- | app/scripts/lib/inpage-provider.js | 5 | ||||
-rw-r--r-- | app/scripts/lib/message-manager.js | 4 | ||||
-rw-r--r-- | app/scripts/lib/migrator/index.js | 43 | ||||
-rw-r--r-- | app/scripts/lib/notification-manager.js | 71 | ||||
-rw-r--r-- | app/scripts/lib/notifications.js | 65 | ||||
-rw-r--r-- | app/scripts/lib/personal-message-manager.js | 4 | ||||
-rw-r--r-- | app/scripts/lib/tx-utils.js | 75 |
18 files changed, 255 insertions, 794 deletions
diff --git a/app/scripts/lib/auto-faucet.js b/app/scripts/lib/auto-faucet.js index 1e86f735e..38d54ba5e 100644 --- a/app/scripts/lib/auto-faucet.js +++ b/app/scripts/lib/auto-faucet.js @@ -3,10 +3,18 @@ const METAMASK_DEBUG = 'GULP_METAMASK_DEBUG' const env = process.env.METAMASK_ENV module.exports = function (address) { - if (METAMASK_DEBUG || env === 'test') return // Don't faucet in development or test - var http = new XMLHttpRequest() - var data = address - http.open('POST', uri, true) - http.setRequestHeader('Content-type', 'application/rawdata') - http.send(data) + // Don't faucet in development or test + if (METAMASK_DEBUG === true || env === 'test') return + global.log.info('auto-fauceting:', address) + const data = address + const headers = new Headers() + headers.append('Content-type', 'application/rawdata') + fetch(uri, { + method: 'POST', + headers, + body: data, + }) + .catch((err) => { + console.error(err) + }) } diff --git a/app/scripts/lib/auto-reload.js b/app/scripts/lib/auto-reload.js index 1302df35f..534047330 100644 --- a/app/scripts/lib/auto-reload.js +++ b/app/scripts/lib/auto-reload.js @@ -1,30 +1,33 @@ -const once = require('once') -const ensnare = require('ensnare') - module.exports = setupDappAutoReload -function setupDappAutoReload (web3) { +function setupDappAutoReload (web3, observable) { // export web3 as a global, checking for usage - var pageIsUsingWeb3 = false - var resetWasRequested = false - global.web3 = ensnare(web3, once(function () { - // if web3 usage happened after a reset request, trigger reset late - if (resetWasRequested) return triggerReset() - // mark web3 as used - pageIsUsingWeb3 = true - // reset web3 reference - global.web3 = web3 - })) + global.web3 = new Proxy(web3, { + get: (_web3, name) => { + // get the time of use + if (name !== '_used') _web3._used = Date.now() + return _web3[name] + }, + set: (_web3, name, value) => { + _web3[name] = value + }, + }) + var networkVersion - return handleResetRequest + observable.subscribe(function (state) { + // get the initial network + const curentNetVersion = state.networkVersion + if (!networkVersion) networkVersion = curentNetVersion - function handleResetRequest () { - resetWasRequested = true - // ignore if web3 was not used - if (!pageIsUsingWeb3) return - // reload after short timeout - setTimeout(triggerReset, 500) - } + if (curentNetVersion !== networkVersion && web3._used) { + const timeSinceUse = Date.now() - web3._used + // if web3 was recently used then delay the reloading of the page + timeSinceUse > 500 ? triggerReset() : setTimeout(triggerReset, 500) + // prevent reentry into if statement if state updates again before + // reload + networkVersion = curentNetVersion + } + }) } // reload the page diff --git a/app/scripts/lib/buy-eth-url.js b/app/scripts/lib/buy-eth-url.js new file mode 100644 index 000000000..b9dde3c28 --- /dev/null +++ b/app/scripts/lib/buy-eth-url.js @@ -0,0 +1,23 @@ +module.exports = getBuyEthUrl + +function getBuyEthUrl ({ network, amount, address }) { + let url + switch (network) { + case '1': + url = `https://buy.coinbase.com/?code=9ec56d01-7e81-5017-930c-513daa27bb6a&amount=${amount}&address=${address}&crypto_currency=ETH` + break + + case '3': + url = 'https://faucet.metamask.io/' + break + + case '4': + url = 'https://www.rinkeby.io/' + break + + case '42': + url = 'https://github.com/kovan-testnet/faucet' + break + } + return url +} diff --git a/app/scripts/lib/config-manager.js b/app/scripts/lib/config-manager.js index 6868637e5..9c0dffe9c 100644 --- a/app/scripts/lib/config-manager.js +++ b/app/scripts/lib/config-manager.js @@ -1,10 +1,12 @@ -const MetamaskConfig = require('../config.js') const ethUtil = require('ethereumjs-util') const normalize = require('eth-sig-util').normalize +const MetamaskConfig = require('../config.js') + -const TESTNET_RPC = MetamaskConfig.network.testnet const MAINNET_RPC = MetamaskConfig.network.mainnet -const MORDEN_RPC = MetamaskConfig.network.morden +const ROPSTEN_RPC = MetamaskConfig.network.ropsten +const KOVAN_RPC = MetamaskConfig.network.kovan +const RINKEBY_RPC = MetamaskConfig.network.rinkeby /* The config-manager is a convenience object * wrapping a pojo-migrator. @@ -32,36 +34,6 @@ ConfigManager.prototype.getConfig = function () { return data.config } -ConfigManager.prototype.setRpcTarget = function (rpcUrl) { - var config = this.getConfig() - config.provider = { - type: 'rpc', - rpcTarget: rpcUrl, - } - this.setConfig(config) -} - -ConfigManager.prototype.setProviderType = function (type) { - var config = this.getConfig() - config.provider = { - type: type, - } - this.setConfig(config) -} - -ConfigManager.prototype.useEtherscanProvider = function () { - var config = this.getConfig() - config.provider = { - type: 'etherscan', - } - this.setConfig(config) -} - -ConfigManager.prototype.getProvider = function () { - var config = this.getConfig() - return config.provider -} - ConfigManager.prototype.setData = function (data) { this.store.putState(data) } @@ -135,6 +107,35 @@ ConfigManager.prototype.getSeedWords = function () { var data = this.getData() return data.seedWords } +ConfigManager.prototype.setRpcTarget = function (rpcUrl) { + var config = this.getConfig() + config.provider = { + type: 'rpc', + rpcTarget: rpcUrl, + } + this.setConfig(config) +} + +ConfigManager.prototype.setProviderType = function (type) { + var config = this.getConfig() + config.provider = { + type: type, + } + this.setConfig(config) +} + +ConfigManager.prototype.useEtherscanProvider = function () { + var config = this.getConfig() + config.provider = { + type: 'etherscan', + } + this.setConfig(config) +} + +ConfigManager.prototype.getProvider = function () { + var config = this.getConfig() + return config.provider +} ConfigManager.prototype.getCurrentRpcAddress = function () { var provider = this.getProvider() @@ -144,14 +145,17 @@ ConfigManager.prototype.getCurrentRpcAddress = function () { case 'mainnet': return MAINNET_RPC - case 'testnet': - return TESTNET_RPC + case 'ropsten': + return ROPSTEN_RPC + + case 'kovan': + return KOVAN_RPC - case 'morden': - return MORDEN_RPC + case 'rinkeby': + return RINKEBY_RPC default: - return provider && provider.rpcTarget ? provider.rpcTarget : TESTNET_RPC + return provider && provider.rpcTarget ? provider.rpcTarget : RINKEBY_RPC } } diff --git a/app/scripts/lib/eth-store.js b/app/scripts/lib/eth-store.js index 8812a507b..ebba98f5c 100644 --- a/app/scripts/lib/eth-store.js +++ b/app/scripts/lib/eth-store.js @@ -10,7 +10,7 @@ const async = require('async') const EthQuery = require('eth-query') const ObservableStore = require('obs-store') -function noop() {} +function noop () {} class EthereumStore extends ObservableStore { @@ -19,6 +19,9 @@ class EthereumStore extends ObservableStore { super({ accounts: {}, transactions: {}, + currentBlockNumber: '0', + currentBlockHash: '', + currentBlockGasLimit: '', }) this._provider = opts.provider this._query = new EthQuery(this._provider) @@ -69,6 +72,9 @@ class EthereumStore extends ObservableStore { _updateForBlock (block) { const blockNumber = '0x' + block.number.toString('hex') this._currentBlockNumber = blockNumber + this.updateState({ currentBlockNumber: parseInt(blockNumber) }) + this.updateState({ currentBlockHash: `0x${block.hash.toString('hex')}`}) + this.updateState({ currentBlockGasLimit: `0x${block.gasLimit.toString('hex')}` }) async.parallel([ this._updateAccounts.bind(this), this._updateTransactions.bind(this, blockNumber), @@ -129,4 +135,4 @@ class EthereumStore extends ObservableStore { } -module.exports = EthereumStore
\ No newline at end of file +module.exports = EthereumStore diff --git a/app/scripts/lib/extension-instance.js b/app/scripts/lib/extension-instance.js deleted file mode 100644 index 628b62e3f..000000000 --- a/app/scripts/lib/extension-instance.js +++ /dev/null @@ -1,68 +0,0 @@ -const apis = [ - 'alarms', - 'bookmarks', - 'browserAction', - 'commands', - 'contextMenus', - 'cookies', - 'downloads', - 'events', - 'extension', - 'extensionTypes', - 'history', - 'i18n', - 'idle', - 'notifications', - 'pageAction', - 'runtime', - 'storage', - 'tabs', - 'webNavigation', - 'webRequest', - 'windows', -] - -function Extension () { - const _this = this - - apis.forEach(function (api) { - - _this[api] = null - - try { - if (chrome[api]) { - _this[api] = chrome[api] - } - } catch (e) {} - - try { - if (window[api]) { - _this[api] = window[api] - } - } catch (e) {} - - try { - if (browser[api]) { - _this[api] = browser[api] - } - } catch (e) {} - try { - _this.api = browser.extension[api] - } catch (e) {} - }) - - try { - if (browser && browser.runtime) { - this.runtime = browser.runtime - } - } catch (e) {} - - try { - if (browser && browser.browserAction) { - this.browserAction = browser.browserAction - } - } catch (e) {} - -} - -module.exports = Extension diff --git a/app/scripts/lib/extension.js b/app/scripts/lib/extension.js deleted file mode 100644 index 4b670490f..000000000 --- a/app/scripts/lib/extension.js +++ /dev/null @@ -1,14 +0,0 @@ -/* Extension.js - * - * A module for unifying browser differences in the WebExtension API. - * - * Initially implemented because Chrome hides all of their WebExtension API - * behind a global `chrome` variable, but we'd like to start grooming - * the code-base for cross-browser extension support. - * - * You can read more about the WebExtension API here: - * https://developer.mozilla.org/en-US/Add-ons/WebExtensions - */ - -const Extension = require('./extension-instance') -module.exports = new Extension() diff --git a/app/scripts/lib/hex-to-bn.js b/app/scripts/lib/hex-to-bn.js new file mode 100644 index 000000000..184217279 --- /dev/null +++ b/app/scripts/lib/hex-to-bn.js @@ -0,0 +1,7 @@ +const ethUtil = require('ethereumjs-util') +const BN = ethUtil.BN + +module.exports = function hexToBn (hex) { + return new BN(ethUtil.stripHexPrefix(hex), 16) +} + diff --git a/app/scripts/lib/id-management.js b/app/scripts/lib/id-management.js deleted file mode 100644 index 90b3fdb13..000000000 --- a/app/scripts/lib/id-management.js +++ /dev/null @@ -1,90 +0,0 @@ -/* ID Management - * - * This module exists to hold the decrypted credentials for the current session. - * It therefore exposes sign methods, because it is able to perform these - * with noa dditional authentication, because its very instantiation - * means the vault is unlocked. - */ - -const ethUtil = require('ethereumjs-util') -const Transaction = require('ethereumjs-tx') - -module.exports = IdManagement - -function IdManagement (opts) { - if (!opts) opts = {} - - this.keyStore = opts.keyStore - this.derivedKey = opts.derivedKey - this.configManager = opts.configManager - this.hdPathString = "m/44'/60'/0'/0" - - this.getAddresses = function () { - return this.keyStore.getAddresses(this.hdPathString).map(function (address) { return '0x' + address }) - } - - this.signTx = function (txParams) { - - // normalize values - txParams.gasPrice = ethUtil.intToHex(txParams.gasPrice) - txParams.to = ethUtil.addHexPrefix(txParams.to) - txParams.from = ethUtil.addHexPrefix(txParams.from.toLowerCase()) - txParams.value = ethUtil.addHexPrefix(txParams.value) - txParams.data = ethUtil.addHexPrefix(txParams.data) - txParams.gasLimit = ethUtil.addHexPrefix(txParams.gasLimit || txParams.gas) - txParams.nonce = ethUtil.addHexPrefix(txParams.nonce) - var tx = new Transaction(txParams) - - // sign tx - var privKeyHex = this.exportPrivateKey(txParams.from) - var privKey = ethUtil.toBuffer(privKeyHex) - tx.sign(privKey) - - // Add the tx hash to the persisted meta-tx object - var txHash = ethUtil.bufferToHex(tx.hash()) - var metaTx = this.configManager.getTx(txParams.metamaskId) - metaTx.hash = txHash - this.configManager.updateTx(metaTx) - - // return raw serialized tx - var rawTx = ethUtil.bufferToHex(tx.serialize()) - return rawTx - } - - this.signMsg = function (address, message) { - // sign message - var privKeyHex = this.exportPrivateKey(address.toLowerCase()) - var privKey = ethUtil.toBuffer(privKeyHex) - var msgSig = ethUtil.ecsign(new Buffer(message.replace('0x', ''), 'hex'), privKey) - var rawMsgSig = ethUtil.bufferToHex(concatSig(msgSig.v, msgSig.r, msgSig.s)) - return rawMsgSig - } - - this.getSeed = function () { - return this.keyStore.getSeed(this.derivedKey) - } - - this.exportPrivateKey = function (address) { - var privKeyHex = ethUtil.addHexPrefix(this.keyStore.exportPrivateKey(address, this.derivedKey, this.hdPathString)) - return privKeyHex - } -} - -function padWithZeroes (number, length) { - var myString = '' + number - while (myString.length < length) { - myString = '0' + myString - } - return myString -} - -function concatSig (v, r, s) { - const rSig = ethUtil.fromSigned(r) - const sSig = ethUtil.fromSigned(s) - const vSig = ethUtil.bufferToInt(v) - const rStr = padWithZeroes(ethUtil.toUnsigned(rSig).toString('hex'), 64) - const sStr = padWithZeroes(ethUtil.toUnsigned(sSig).toString('hex'), 64) - const vStr = ethUtil.stripHexPrefix(ethUtil.intToHex(vSig)) - return ethUtil.addHexPrefix(rStr.concat(sStr, vStr)).toString('hex') -} - diff --git a/app/scripts/lib/idStore-migrator.js b/app/scripts/lib/idStore-migrator.js deleted file mode 100644 index 62d21eee7..000000000 --- a/app/scripts/lib/idStore-migrator.js +++ /dev/null @@ -1,80 +0,0 @@ -const IdentityStore = require('./idStore') -const HdKeyring = require('eth-hd-keyring') -const sigUtil = require('eth-sig-util') -const normalize = sigUtil.normalize -const denodeify = require('denodeify') - -module.exports = class IdentityStoreMigrator { - - constructor ({ configManager }) { - this.configManager = configManager - const hasOldVault = this.hasOldVault() - if (!hasOldVault) { - this.idStore = new IdentityStore({ configManager }) - } - } - - migratedVaultForPassword (password) { - const hasOldVault = this.hasOldVault() - const configManager = this.configManager - - if (!this.idStore) { - this.idStore = new IdentityStore({ configManager }) - } - - if (!hasOldVault) { - return Promise.resolve(null) - } - - const idStore = this.idStore - const submitPassword = denodeify(idStore.submitPassword.bind(idStore)) - - return submitPassword(password) - .then(() => { - const serialized = this.serializeVault() - return this.checkForLostAccounts(serialized) - }) - } - - serializeVault () { - const mnemonic = this.idStore._idmgmt.getSeed() - const numberOfAccounts = this.idStore._getAddresses().length - - return { - type: 'HD Key Tree', - data: { mnemonic, numberOfAccounts }, - } - } - - checkForLostAccounts (serialized) { - const hd = new HdKeyring() - return hd.deserialize(serialized.data) - .then((hexAccounts) => { - const newAccounts = hexAccounts.map(normalize) - const oldAccounts = this.idStore._getAddresses().map(normalize) - const lostAccounts = oldAccounts.reduce((result, account) => { - if (newAccounts.includes(account)) { - return result - } else { - result.push(account) - return result - } - }, []) - - return { - serialized, - lostAccounts: lostAccounts.map((address) => { - return { - address, - privateKey: this.idStore.exportAccount(address), - } - }), - } - }) - } - - hasOldVault () { - const wallet = this.configManager.getWallet() - return wallet - } -} diff --git a/app/scripts/lib/idStore.js b/app/scripts/lib/idStore.js deleted file mode 100644 index 01474035e..000000000 --- a/app/scripts/lib/idStore.js +++ /dev/null @@ -1,343 +0,0 @@ -const EventEmitter = require('events').EventEmitter -const inherits = require('util').inherits -const ethUtil = require('ethereumjs-util') -const KeyStore = require('eth-lightwallet').keystore -const clone = require('clone') -const extend = require('xtend') -const autoFaucet = require('./auto-faucet') -const DEFAULT_RPC = 'https://testrpc.metamask.io/' -const IdManagement = require('./id-management') - - -module.exports = IdentityStore - -inherits(IdentityStore, EventEmitter) -function IdentityStore (opts = {}) { - EventEmitter.call(this) - - // we just use the ethStore to auto-add accounts - this._ethStore = opts.ethStore - this.configManager = opts.configManager - // lightwallet key store - this._keyStore = null - // lightwallet wrapper - this._idmgmt = null - - this.hdPathString = "m/44'/60'/0'/0" - - this._currentState = { - selectedAddress: null, - identities: {}, - } - // not part of serilized metamask state - only kept in memory -} - -// -// public -// - -IdentityStore.prototype.createNewVault = function (password, cb) { - delete this._keyStore - var serializedKeystore = this.configManager.getWallet() - - if (serializedKeystore) { - this.configManager.setData({}) - } - - this.purgeCache() - this._createVault(password, null, (err) => { - if (err) return cb(err) - - this._autoFaucet() - - this.configManager.setShowSeedWords(true) - var seedWords = this._idmgmt.getSeed() - - this._loadIdentities() - - cb(null, seedWords) - }) -} - -IdentityStore.prototype.recoverSeed = function (cb) { - this.configManager.setShowSeedWords(true) - if (!this._idmgmt) return cb(new Error('Unauthenticated. Please sign in.')) - var seedWords = this._idmgmt.getSeed() - cb(null, seedWords) -} - -IdentityStore.prototype.recoverFromSeed = function (password, seed, cb) { - this.purgeCache() - - this._createVault(password, seed, (err) => { - if (err) return cb(err) - - this._loadIdentities() - cb(null, this.getState()) - }) -} - -IdentityStore.prototype.setStore = function (store) { - this._ethStore = store -} - -IdentityStore.prototype.clearSeedWordCache = function (cb) { - const configManager = this.configManager - configManager.setShowSeedWords(false) - cb(null, configManager.getSelectedAccount()) -} - -IdentityStore.prototype.getState = function () { - const configManager = this.configManager - var seedWords = this.getSeedIfUnlocked() - return clone(extend(this._currentState, { - isInitialized: !!configManager.getWallet() && !seedWords, - isUnlocked: this._isUnlocked(), - seedWords: seedWords, - selectedAddress: configManager.getSelectedAccount(), - })) -} - -IdentityStore.prototype.getSeedIfUnlocked = function () { - const configManager = this.configManager - var showSeed = configManager.getShouldShowSeedWords() - var idmgmt = this._idmgmt - var shouldShow = showSeed && !!idmgmt - var seedWords = shouldShow ? idmgmt.getSeed() : null - return seedWords -} - -IdentityStore.prototype.getSelectedAddress = function () { - const configManager = this.configManager - return configManager.getSelectedAccount() -} - -IdentityStore.prototype.setSelectedAddressSync = function (address) { - const configManager = this.configManager - if (!address) { - var addresses = this._getAddresses() - address = addresses[0] - } - - configManager.setSelectedAccount(address) - return address -} - -IdentityStore.prototype.setSelectedAddress = function (address, cb) { - const resultAddress = this.setSelectedAddressSync(address) - if (cb) return cb(null, resultAddress) -} - -IdentityStore.prototype.revealAccount = function (cb) { - const derivedKey = this._idmgmt.derivedKey - const keyStore = this._keyStore - const configManager = this.configManager - - keyStore.setDefaultHdDerivationPath(this.hdPathString) - keyStore.generateNewAddress(derivedKey, 1) - const addresses = keyStore.getAddresses() - const address = addresses[ addresses.length - 1 ] - - this._ethStore.addAccount(ethUtil.addHexPrefix(address)) - - configManager.setWallet(keyStore.serialize()) - - this._loadIdentities() - this._didUpdate() - cb(null) -} - -IdentityStore.prototype.getNetwork = function (err) { - if (err) { - this._currentState.network = 'loading' - this._didUpdate() - } - - this.web3.version.getNetwork((err, network) => { - if (err) { - this._currentState.network = 'loading' - return this._didUpdate() - } - if (global.METAMASK_DEBUG) { - console.log('web3.getNetwork returned ' + network) - } - this._currentState.network = network - this._didUpdate() - }) -} - -IdentityStore.prototype.setLocked = function (cb) { - delete this._keyStore - delete this._idmgmt - cb() -} - -IdentityStore.prototype.submitPassword = function (password, cb) { - const configManager = this.configManager - this.tryPassword(password, (err) => { - if (err) return cb(err) - // load identities before returning... - this._loadIdentities() - cb(null, configManager.getSelectedAccount()) - }) -} - -IdentityStore.prototype.exportAccount = function (address, cb) { - var privateKey = this._idmgmt.exportPrivateKey(address) - if (cb) cb(null, privateKey) - return privateKey -} - -// private -// - -IdentityStore.prototype._didUpdate = function () { - this.emit('update', this.getState()) -} - -IdentityStore.prototype._isUnlocked = function () { - var result = Boolean(this._keyStore) && Boolean(this._idmgmt) - return result -} - -// load identities from keyStoreet -IdentityStore.prototype._loadIdentities = function () { - const configManager = this.configManager - if (!this._isUnlocked()) throw new Error('not unlocked') - - var addresses = this._getAddresses() - addresses.forEach((address, i) => { - // // add to ethStore - if (this._ethStore) { - this._ethStore.addAccount(ethUtil.addHexPrefix(address)) - } - // add to identities - const defaultLabel = 'Account ' + (i + 1) - const nickname = configManager.nicknameForWallet(address) - var identity = { - name: nickname || defaultLabel, - address: address, - mayBeFauceting: this._mayBeFauceting(i), - } - this._currentState.identities[address] = identity - }) - this._didUpdate() -} - -IdentityStore.prototype.saveAccountLabel = function (account, label, cb) { - const configManager = this.configManager - configManager.setNicknameForWallet(account, label) - this._loadIdentities() - cb(null, label) -} - -// mayBeFauceting -// If on testnet, index 0 may be fauceting. -// The UI will have to check the balance to know. -// If there is no balance and it mayBeFauceting, -// then it is in fact fauceting. -IdentityStore.prototype._mayBeFauceting = function (i) { - const configManager = this.configManager - var config = configManager.getProvider() - if (i === 0 && - config.type === 'rpc' && - config.rpcTarget === DEFAULT_RPC) { - return true - } - return false -} - -// -// keyStore managment - unlocking + deserialization -// - -IdentityStore.prototype.tryPassword = function (password, cb) { - var serializedKeystore = this.configManager.getWallet() - var keyStore = KeyStore.deserialize(serializedKeystore) - - keyStore.keyFromPassword(password, (err, pwDerivedKey) => { - if (err) return cb(err) - - const isCorrect = keyStore.isDerivedKeyCorrect(pwDerivedKey) - if (!isCorrect) return cb(new Error('Lightwallet - password incorrect')) - - this._keyStore = keyStore - this._createIdMgmt(pwDerivedKey) - cb() - }) -} - -IdentityStore.prototype._createVault = function (password, seedPhrase, cb) { - const opts = { - password, - hdPathString: this.hdPathString, - } - - if (seedPhrase) { - opts.seedPhrase = seedPhrase - } - - KeyStore.createVault(opts, (err, keyStore) => { - if (err) return cb(err) - - this._keyStore = keyStore - - keyStore.keyFromPassword(password, (err, derivedKey) => { - if (err) return cb(err) - - this.purgeCache() - - keyStore.addHdDerivationPath(this.hdPathString, derivedKey, {curve: 'secp256k1', purpose: 'sign'}) - - this._createFirstWallet(derivedKey) - this._createIdMgmt(derivedKey) - this.setSelectedAddressSync() - - cb() - }) - }) -} - -IdentityStore.prototype._createIdMgmt = function (derivedKey) { - this._idmgmt = new IdManagement({ - keyStore: this._keyStore, - derivedKey: derivedKey, - configManager: this.configManager, - }) -} - -IdentityStore.prototype.purgeCache = function () { - this._currentState.identities = {} - let accounts - try { - accounts = Object.keys(this._ethStore._currentState.accounts) - } catch (e) { - accounts = [] - } - accounts.forEach((address) => { - this._ethStore.removeAccount(address) - }) -} - -IdentityStore.prototype._createFirstWallet = function (derivedKey) { - const keyStore = this._keyStore - keyStore.setDefaultHdDerivationPath(this.hdPathString) - keyStore.generateNewAddress(derivedKey, 1) - this.configManager.setWallet(keyStore.serialize()) - var addresses = keyStore.getAddresses() - this._ethStore.addAccount(ethUtil.addHexPrefix(addresses[0])) -} - -// get addresses and normalize address hexString -IdentityStore.prototype._getAddresses = function () { - return this._keyStore.getAddresses(this.hdPathString).map((address) => { - return ethUtil.addHexPrefix(address) - }) -} - -IdentityStore.prototype._autoFaucet = function () { - var addresses = this._getAddresses() - autoFaucet(addresses[0]) -} - -// util diff --git a/app/scripts/lib/inpage-provider.js b/app/scripts/lib/inpage-provider.js index 92936de2f..8b8623974 100644 --- a/app/scripts/lib/inpage-provider.js +++ b/app/scripts/lib/inpage-provider.js @@ -34,6 +34,7 @@ function MetamaskInpageProvider (connectionStream) { asyncProvider, (err) => logStreamDisconnectWarning('MetaMask RpcProvider', err) ) + // start and stop polling to unblock first block lock self.idMap = {} // handle sendAsync requests via asyncProvider @@ -85,7 +86,7 @@ MetamaskInpageProvider.prototype.send = function (payload) { break case 'net_version': - let networkVersion = self.publicConfigStore.getState().networkVersion + const networkVersion = self.publicConfigStore.getState().networkVersion result = networkVersion break @@ -125,7 +126,7 @@ function eachJsonMessage (payload, transformFn) { } } -function logStreamDisconnectWarning(remoteLabel, err){ +function logStreamDisconnectWarning (remoteLabel, err) { let warningMsg = `MetamaskInpageProvider - lost connection to ${remoteLabel}` if (err) warningMsg += '\n' + err.stack console.warn(warningMsg) diff --git a/app/scripts/lib/message-manager.js b/app/scripts/lib/message-manager.js index 711d5f159..f52e048e0 100644 --- a/app/scripts/lib/message-manager.js +++ b/app/scripts/lib/message-manager.js @@ -4,7 +4,7 @@ const ethUtil = require('ethereumjs-util') const createId = require('./random-id') -module.exports = class MessageManager extends EventEmitter{ +module.exports = class MessageManager extends EventEmitter { constructor (opts) { super() this.memStore = new ObservableStore({ @@ -108,7 +108,7 @@ module.exports = class MessageManager extends EventEmitter{ } -function normalizeMsgData(data) { +function normalizeMsgData (data) { if (data.slice(0, 2) === '0x') { // data is already hex return data diff --git a/app/scripts/lib/migrator/index.js b/app/scripts/lib/migrator/index.js index 312345263..de6f5d5cd 100644 --- a/app/scripts/lib/migrator/index.js +++ b/app/scripts/lib/migrator/index.js @@ -1,42 +1,35 @@ -const asyncQ = require('async-q') - class Migrator { constructor (opts = {}) { - let migrations = opts.migrations || [] + const migrations = opts.migrations || [] + // sort migrations by version this.migrations = migrations.sort((a, b) => a.version - b.version) - let lastMigration = this.migrations.slice(-1)[0] + // grab migration with highest version + const lastMigration = this.migrations.slice(-1)[0] // use specified defaultVersion or highest migration version this.defaultVersion = opts.defaultVersion || (lastMigration && lastMigration.version) || 0 } // run all pending migrations on meta in place - migrateData (versionedData = this.generateInitialState()) { - let remaining = this.migrations.filter(migrationIsPending) - - return ( - asyncQ.eachSeries(remaining, (migration) => this.runMigration(versionedData, migration)) - .then(() => versionedData) - ) - - // migration is "pending" if hit has a higher + async migrateData (versionedData = this.generateInitialState()) { + const pendingMigrations = this.migrations.filter(migrationIsPending) + + for (let index in pendingMigrations) { + let 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') + } + + return versionedData + + // migration is "pending" if it has a higher // version number than currentVersion - function migrationIsPending(migration) { + function migrationIsPending (migration) { return migration.version > versionedData.meta.version } } - runMigration(versionedData, migration) { - return ( - migration.migrate(versionedData) - .then((versionedData) => { - if (!versionedData.data) return Promise.reject(new Error('Migrator - Migration returned empty data')) - if (migration.version !== undefined && versionedData.meta.version !== migration.version) return Promise.reject(new Error('Migrator - Migration did not update version number correctly')) - return Promise.resolve(versionedData) - }) - ) - } - generateInitialState (initState) { return { meta: { diff --git a/app/scripts/lib/notification-manager.js b/app/scripts/lib/notification-manager.js new file mode 100644 index 000000000..7846ef7f0 --- /dev/null +++ b/app/scripts/lib/notification-manager.js @@ -0,0 +1,71 @@ +const extension = require('extensionizer') +const height = 520 +const width = 360 + + +class NotificationManager { + + // + // Public + // + + showPopup () { + this._getPopup((err, popup) => { + if (err) throw err + + if (popup) { + // bring focus to existing popup + extension.windows.update(popup.id, { focused: true }) + } else { + // create new popup + extension.windows.create({ + url: 'notification.html', + type: 'popup', + width, + height, + }) + } + }) + } + + closePopup () { + this._getPopup((err, popup) => { + if (err) throw err + if (!popup) return + extension.windows.remove(popup.id, console.error) + }) + } + + // + // Private + // + + _getPopup (cb) { + this._getWindows((err, windows) => { + if (err) throw err + cb(null, this._getPopupIn(windows)) + }) + } + + _getWindows (cb) { + // Ignore in test environment + if (!extension.windows) { + return cb() + } + + extension.windows.getAll({}, (windows) => { + cb(null, windows) + }) + } + + _getPopupIn (windows) { + return windows ? windows.find((win) => { + return (win && win.type === 'popup' && + win.height === height && + win.width === width) + }) : null + } + +} + +module.exports = NotificationManager diff --git a/app/scripts/lib/notifications.js b/app/scripts/lib/notifications.js deleted file mode 100644 index 3db1ac6b5..000000000 --- a/app/scripts/lib/notifications.js +++ /dev/null @@ -1,65 +0,0 @@ -const extension = require('./extension') -const height = 520 -const width = 360 - -const notifications = { - show, - getPopup, - closePopup, -} -module.exports = notifications -window.METAMASK_NOTIFIER = notifications - -function show () { - getPopup((err, popup) => { - if (err) throw err - - if (popup) { - // bring focus to existing popup - extension.windows.update(popup.id, { focused: true }) - } else { - // create new popup - extension.windows.create({ - url: 'notification.html', - type: 'popup', - focused: true, - width, - height, - }) - } - }) -} - -function getWindows (cb) { - // Ignore in test environment - if (!extension.windows) { - return cb() - } - - extension.windows.getAll({}, (windows) => { - cb(null, windows) - }) -} - -function getPopup (cb) { - getWindows((err, windows) => { - if (err) throw err - cb(null, getPopupIn(windows)) - }) -} - -function getPopupIn (windows) { - return windows ? windows.find((win) => { - return (win && win.type === 'popup' && - win.height === height && - win.width === width) - }) : null -} - -function closePopup () { - getPopup((err, popup) => { - if (err) throw err - if (!popup) return - extension.windows.remove(popup.id, console.error) - }) -} diff --git a/app/scripts/lib/personal-message-manager.js b/app/scripts/lib/personal-message-manager.js index bbc978446..6602f5aa8 100644 --- a/app/scripts/lib/personal-message-manager.js +++ b/app/scripts/lib/personal-message-manager.js @@ -5,7 +5,7 @@ const createId = require('./random-id') const hexRe = /^[0-9A-Fa-f]+$/g -module.exports = class PersonalMessageManager extends EventEmitter{ +module.exports = class PersonalMessageManager extends EventEmitter { constructor (opts) { super() this.memStore = new ObservableStore({ @@ -108,7 +108,7 @@ module.exports = class PersonalMessageManager extends EventEmitter{ this.emit('updateBadge') } - normalizeMsgData(data) { + normalizeMsgData (data) { try { const stripped = ethUtil.stripHexPrefix(data) if (stripped.match(hexRe)) { diff --git a/app/scripts/lib/tx-utils.js b/app/scripts/lib/tx-utils.js index c6814c05f..149d93102 100644 --- a/app/scripts/lib/tx-utils.js +++ b/app/scripts/lib/tx-utils.js @@ -1,5 +1,4 @@ const async = require('async') -const EthQuery = require('eth-query') const ethUtil = require('ethereumjs-util') const Transaction = require('ethereumjs-tx') const normalize = require('eth-sig-util').normalize @@ -7,53 +6,55 @@ const BN = ethUtil.BN /* tx-utils are utility methods for Transaction manager -its passed a provider and that is passed to ethquery +its passed ethquery and used to do things like calculate gas of a tx. */ module.exports = class txProviderUtils { - constructor (provider) { - this.provider = provider - this.query = new EthQuery(provider) + + constructor (ethQuery) { + this.query = ethQuery } - analyzeGasUsage (txData, cb) { + analyzeGasUsage (txMeta, cb) { var self = this this.query.getBlockByNumber('latest', true, (err, block) => { if (err) return cb(err) async.waterfall([ - self.estimateTxGas.bind(self, txData, block.gasLimit), - self.setTxGas.bind(self, txData, block.gasLimit), + self.estimateTxGas.bind(self, txMeta, block.gasLimit), + self.setTxGas.bind(self, txMeta, block.gasLimit), ], cb) }) } - estimateTxGas (txData, blockGasLimitHex, cb) { - const txParams = txData.txParams + estimateTxGas (txMeta, blockGasLimitHex, cb) { + const txParams = txMeta.txParams // check if gasLimit is already specified - txData.gasLimitSpecified = Boolean(txParams.gas) + txMeta.gasLimitSpecified = Boolean(txParams.gas) // if not, fallback to block gasLimit - if (!txData.gasLimitSpecified) { - txParams.gas = blockGasLimitHex + if (!txMeta.gasLimitSpecified) { + const blockGasLimitBN = hexToBn(blockGasLimitHex) + const saferGasLimitBN = BnMultiplyByFraction(blockGasLimitBN, 19, 20) + txParams.gas = bnToHex(saferGasLimitBN) } // run tx, see if it will OOG this.query.estimateGas(txParams, cb) } - setTxGas (txData, blockGasLimitHex, estimatedGasHex, cb) { - txData.estimatedGas = estimatedGasHex - const txParams = txData.txParams + setTxGas (txMeta, blockGasLimitHex, estimatedGasHex, cb) { + txMeta.estimatedGas = estimatedGasHex + const txParams = txMeta.txParams // if gasLimit was specified and doesnt OOG, // use original specified amount - if (txData.gasLimitSpecified) { - txData.estimatedGas = txParams.gas + if (txMeta.gasLimitSpecified) { + txMeta.estimatedGas = txParams.gas cb() return } // if gasLimit not originally specified, // try adding an additional gas buffer to our estimation for safety - const recommendedGasHex = this.addGasBuffer(txData.estimatedGas, blockGasLimitHex) + const recommendedGasHex = this.addGasBuffer(txMeta.estimatedGas, blockGasLimitHex) txParams.gas = recommendedGasHex cb() return @@ -62,25 +63,26 @@ module.exports = class txProviderUtils { addGasBuffer (initialGasLimitHex, blockGasLimitHex) { const initialGasLimitBn = hexToBn(initialGasLimitHex) const blockGasLimitBn = hexToBn(blockGasLimitHex) + const upperGasLimitBn = blockGasLimitBn.muln(0.9) const bufferedGasLimitBn = initialGasLimitBn.muln(1.5) - + // if initialGasLimit is above blockGasLimit, dont modify it - if (initialGasLimitBn.gt(blockGasLimitBn)) return bnToHex(initialGasLimitBn) + if (initialGasLimitBn.gt(upperGasLimitBn)) return bnToHex(initialGasLimitBn) // if bufferedGasLimit is below blockGasLimit, use bufferedGasLimit - if (bufferedGasLimitBn.lt(blockGasLimitBn)) return bnToHex(bufferedGasLimitBn) + if (bufferedGasLimitBn.lt(upperGasLimitBn)) return bnToHex(bufferedGasLimitBn) // otherwise use blockGasLimit - return bnToHex(blockGasLimitBn) + return bnToHex(upperGasLimitBn) } fillInTxParams (txParams, cb) { - let fromAddress = txParams.from - let reqs = {} + const fromAddress = txParams.from + const reqs = {} if (isUndef(txParams.gas)) reqs.gas = (cb) => this.query.estimateGas(txParams, cb) if (isUndef(txParams.gasPrice)) reqs.gasPrice = (cb) => this.query.gasPrice(cb) if (isUndef(txParams.nonce)) reqs.nonce = (cb) => this.query.getTransactionCount(fromAddress, 'pending', cb) - async.parallel(reqs, function(err, result) { + async.parallel(reqs, function (err, result) { if (err) return cb(err) // write results to txParams obj Object.assign(txParams, result) @@ -90,16 +92,13 @@ module.exports = class txProviderUtils { // builds ethTx from txParams object buildEthTxFromParams (txParams) { - // apply gas multiplyer - let gasPrice = hexToBn(txParams.gasPrice) - // multiply and divide by 100 so as to add percision to integer mul - txParams.gasPrice = ethUtil.intToHex(gasPrice.toNumber()) // normalize values txParams.to = normalize(txParams.to) txParams.from = normalize(txParams.from) txParams.value = normalize(txParams.value) txParams.data = normalize(txParams.data) - txParams.gasLimit = normalize(txParams.gasLimit || txParams.gas) + txParams.gas = normalize(txParams.gas || txParams.gasLimit) + txParams.gasPrice = normalize(txParams.gasPrice) txParams.nonce = normalize(txParams.nonce) // build ethTx log.info(`Prepared tx for signing: ${JSON.stringify(txParams)}`) @@ -124,14 +123,20 @@ module.exports = class txProviderUtils { // util -function isUndef(value) { +function isUndef (value) { return value === undefined } -function bnToHex(inputBn) { +function bnToHex (inputBn) { return ethUtil.addHexPrefix(inputBn.toString(16)) } -function hexToBn(inputHex) { +function hexToBn (inputHex) { return new BN(ethUtil.stripHexPrefix(inputHex), 16) -}
\ No newline at end of file +} + +function BnMultiplyByFraction (targetBN, numerator, denominator) { + const numBN = new BN(numerator) + const denomBN = new BN(denominator) + return targetBN.mul(numBN).div(denomBN) +} |