mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 09:52:26 +01:00
Merge branch 'dev' into obs-store2
This commit is contained in:
commit
74dc20bdf1
@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"parserOptions": {
|
"parserOptions": {
|
||||||
|
"sourceType": "module",
|
||||||
"ecmaVersion": 6,
|
"ecmaVersion": 6,
|
||||||
"ecmaFeatures": {
|
"ecmaFeatures": {
|
||||||
"experimentalObjectRestSpread": true,
|
"experimentalObjectRestSpread": true,
|
||||||
@ -44,7 +45,7 @@
|
|||||||
"eol-last": 1,
|
"eol-last": 1,
|
||||||
"eqeqeq": [2, "allow-null"],
|
"eqeqeq": [2, "allow-null"],
|
||||||
"generator-star-spacing": [2, { "before": true, "after": true }],
|
"generator-star-spacing": [2, { "before": true, "after": true }],
|
||||||
"handle-callback-err": [2, "^(err|error)$" ],
|
"handle-callback-err": [1, "^(err|error)$" ],
|
||||||
"indent": [2, 2, { "SwitchCase": 1 }],
|
"indent": [2, 2, { "SwitchCase": 1 }],
|
||||||
"jsx-quotes": [2, "prefer-single"],
|
"jsx-quotes": [2, "prefer-single"],
|
||||||
"key-spacing": [2, { "beforeColon": false, "afterColon": true }],
|
"key-spacing": [2, { "beforeColon": false, "afterColon": true }],
|
||||||
@ -145,6 +146,6 @@
|
|||||||
"wrap-iife": [2, "any"],
|
"wrap-iife": [2, "any"],
|
||||||
"yield-star-spacing": [2, "both"],
|
"yield-star-spacing": [2, "both"],
|
||||||
"yoda": [2, "never"],
|
"yoda": [2, "never"],
|
||||||
"prefer-const": 1
|
"prefer-const": 1,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
22
CHANGELOG.md
22
CHANGELOG.md
@ -2,6 +2,26 @@
|
|||||||
|
|
||||||
## Current Master
|
## Current Master
|
||||||
|
|
||||||
|
- Add ability to import accounts in JSON file format (used by Mist, Geth, MyEtherWallet, and more!)
|
||||||
|
## 3.1.1 2017-1-20
|
||||||
|
|
||||||
|
- Fix HD wallet seed export
|
||||||
|
|
||||||
|
## 3.1.0 2017-1-18
|
||||||
|
|
||||||
|
- Add ability to import accounts by private key.
|
||||||
|
- Fixed bug that returned the wrong transaction hashes on private networks that had not implemented EIP 155 replay protection (like TestRPC).
|
||||||
|
|
||||||
|
## 3.0.1 2017-1-17
|
||||||
|
|
||||||
|
- Fixed bug that prevented eth.sign from working.
|
||||||
|
- Fix the displaying of transactions that have been submitted to the network in Transaction History
|
||||||
|
|
||||||
|
## 3.0.0 2017-1-16
|
||||||
|
|
||||||
|
- Fix seed word account generation (https://medium.com/metamask/metamask-3-migration-guide-914b79533cdd#.t4i1qmmsz).
|
||||||
|
- Fix Bug where you see a empty transaction flash by on the confirm transaction view.
|
||||||
|
- Create visible difference in transaction history between a approved but not yet included in a block transaction and a transaction who has been confirmed.
|
||||||
- Fix memory leak in RPC Cache
|
- Fix memory leak in RPC Cache
|
||||||
- Override RPC commands eth_syncing and web3_clientVersion
|
- Override RPC commands eth_syncing and web3_clientVersion
|
||||||
- Remove certain non-essential permissions from certain builds.
|
- Remove certain non-essential permissions from certain builds.
|
||||||
@ -14,6 +34,8 @@
|
|||||||
|
|
||||||
## 2.14.1 2016-12-20
|
## 2.14.1 2016-12-20
|
||||||
|
|
||||||
|
- Update Coinbase info. and increase the buy amount to $15
|
||||||
|
- Fixed ropsten transaction links
|
||||||
- Temporarily disable extension reload detection causing infinite reload bug.
|
- Temporarily disable extension reload detection causing infinite reload bug.
|
||||||
- Implemented basic checking for valid RPC URIs.
|
- Implemented basic checking for valid RPC URIs.
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "MetaMask",
|
"name": "MetaMask",
|
||||||
"short_name": "Metamask",
|
"short_name": "Metamask",
|
||||||
"version": "2.14.1",
|
"version": "3.1.1",
|
||||||
"manifest_version": 2,
|
"manifest_version": 2,
|
||||||
"author": "https://metamask.io",
|
"author": "https://metamask.io",
|
||||||
"description": "Ethereum Browser Extension",
|
"description": "Ethereum Browser Extension",
|
||||||
|
45
app/scripts/account-import-strategies/index.js
Normal file
45
app/scripts/account-import-strategies/index.js
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
const Wallet = require('ethereumjs-wallet')
|
||||||
|
const importers = require('ethereumjs-wallet/thirdparty')
|
||||||
|
const ethUtil = require('ethereumjs-util')
|
||||||
|
|
||||||
|
const accountImporter = {
|
||||||
|
|
||||||
|
importAccount(strategy, args) {
|
||||||
|
try {
|
||||||
|
const importer = this.strategies[strategy]
|
||||||
|
const privateKeyHex = importer.apply(null, args)
|
||||||
|
return Promise.resolve(privateKeyHex)
|
||||||
|
} catch (e) {
|
||||||
|
return Promise.reject(e)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
strategies: {
|
||||||
|
'Private Key': (privateKey) => {
|
||||||
|
const stripped = ethUtil.stripHexPrefix(privateKey)
|
||||||
|
return stripped
|
||||||
|
},
|
||||||
|
'JSON File': (input, password) => {
|
||||||
|
let wallet
|
||||||
|
try {
|
||||||
|
wallet = importers.fromEtherWallet(input, password)
|
||||||
|
} catch (e) {
|
||||||
|
console.log('Attempt to import as EtherWallet format failed, trying V3...')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!wallet) {
|
||||||
|
wallet = Wallet.fromV3(input, password, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
return walletToPrivateKey(wallet)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function walletToPrivateKey (wallet) {
|
||||||
|
const privateKeyBuffer = wallet.getPrivateKey()
|
||||||
|
return ethUtil.bufferToHex(privateKeyBuffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = accountImporter
|
@ -16,6 +16,7 @@ const firstTimeState = require('./first-time-state')
|
|||||||
|
|
||||||
const STORAGE_KEY = 'metamask-config'
|
const STORAGE_KEY = 'metamask-config'
|
||||||
const METAMASK_DEBUG = 'GULP_METAMASK_DEBUG'
|
const METAMASK_DEBUG = 'GULP_METAMASK_DEBUG'
|
||||||
|
|
||||||
let popupIsOpen = false
|
let popupIsOpen = false
|
||||||
|
|
||||||
// state persistence
|
// state persistence
|
||||||
@ -135,6 +136,7 @@ function setupController (initState) {
|
|||||||
// User Interface setup
|
// User Interface setup
|
||||||
//
|
//
|
||||||
|
|
||||||
|
updateBadge()
|
||||||
controller.txManager.on('updateBadge', updateBadge)
|
controller.txManager.on('updateBadge', updateBadge)
|
||||||
|
|
||||||
// plugin badge text
|
// plugin badge text
|
||||||
|
@ -95,7 +95,6 @@ module.exports = class KeyringController extends EventEmitter {
|
|||||||
isInitialized: (!!wallet || !!vault),
|
isInitialized: (!!wallet || !!vault),
|
||||||
isUnlocked: Boolean(this.password),
|
isUnlocked: Boolean(this.password),
|
||||||
isDisclaimerConfirmed: this.configManager.getConfirmedDisclaimer(),
|
isDisclaimerConfirmed: this.configManager.getConfirmedDisclaimer(),
|
||||||
transactions: this.configManager.getTxList(),
|
|
||||||
unconfMsgs: messageManager.unconfirmedMsgs(),
|
unconfMsgs: messageManager.unconfirmedMsgs(),
|
||||||
messages: messageManager.getMsgList(),
|
messages: messageManager.getMsgList(),
|
||||||
selectedAccount: address,
|
selectedAccount: address,
|
||||||
@ -173,7 +172,9 @@ module.exports = class KeyringController extends EventEmitter {
|
|||||||
// Used when creating a first vault, to allow confirmation.
|
// Used when creating a first vault, to allow confirmation.
|
||||||
// Also used when revealing the seed words in the confirmation view.
|
// Also used when revealing the seed words in the confirmation view.
|
||||||
placeSeedWords () {
|
placeSeedWords () {
|
||||||
const firstKeyring = this.keyrings[0]
|
const hdKeyrings = this.keyrings.filter((keyring) => keyring.type === 'HD Key Tree')
|
||||||
|
const firstKeyring = hdKeyrings[0]
|
||||||
|
if (!firstKeyring) throw new Error('KeyringController - No HD Key Tree found')
|
||||||
return firstKeyring.serialize()
|
return firstKeyring.serialize()
|
||||||
.then((serialized) => {
|
.then((serialized) => {
|
||||||
const seedWords = serialized.mnemonic
|
const seedWords = serialized.mnemonic
|
||||||
@ -235,7 +236,10 @@ module.exports = class KeyringController extends EventEmitter {
|
|||||||
addNewKeyring (type, opts) {
|
addNewKeyring (type, opts) {
|
||||||
const Keyring = this.getKeyringClassForType(type)
|
const Keyring = this.getKeyringClassForType(type)
|
||||||
const keyring = new Keyring(opts)
|
const keyring = new Keyring(opts)
|
||||||
return keyring.getAccounts()
|
return keyring.deserialize(opts)
|
||||||
|
.then(() => {
|
||||||
|
return keyring.getAccounts()
|
||||||
|
})
|
||||||
.then((accounts) => {
|
.then((accounts) => {
|
||||||
this.keyrings.push(keyring)
|
this.keyrings.push(keyring)
|
||||||
return this.setupAccounts(accounts)
|
return this.setupAccounts(accounts)
|
||||||
@ -317,13 +321,11 @@ module.exports = class KeyringController extends EventEmitter {
|
|||||||
// This method signs tx and returns a promise for
|
// This method signs tx and returns a promise for
|
||||||
// TX Manager to update the state after signing
|
// TX Manager to update the state after signing
|
||||||
|
|
||||||
signTransaction (ethTx, selectedAddress, txId) {
|
signTransaction (ethTx, _fromAddress) {
|
||||||
const address = normalize(selectedAddress)
|
const fromAddress = normalize(_fromAddress)
|
||||||
return this.getKeyringForAccount(address)
|
return this.getKeyringForAccount(fromAddress)
|
||||||
.then((keyring) => {
|
.then((keyring) => {
|
||||||
return keyring.signTransaction(address, ethTx)
|
return keyring.signTransaction(fromAddress, ethTx)
|
||||||
}).then((tx) => {
|
|
||||||
return {tx, txId}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
// Add Unconfirmed Message
|
// Add Unconfirmed Message
|
||||||
@ -400,6 +402,7 @@ module.exports = class KeyringController extends EventEmitter {
|
|||||||
}).then((rawSig) => {
|
}).then((rawSig) => {
|
||||||
cb(null, rawSig)
|
cb(null, rawSig)
|
||||||
approvalCb(null, true)
|
approvalCb(null, true)
|
||||||
|
messageManager.confirmMsg(msgId)
|
||||||
return rawSig
|
return rawSig
|
||||||
})
|
})
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -76,7 +76,7 @@ class HdKeyring extends EventEmitter {
|
|||||||
// For eth_sign, we need to sign transactions:
|
// For eth_sign, we need to sign transactions:
|
||||||
signMessage (withAccount, data) {
|
signMessage (withAccount, data) {
|
||||||
const wallet = this._getWalletForAccount(withAccount)
|
const wallet = this._getWalletForAccount(withAccount)
|
||||||
const message = ethUtil.removeHexPrefix(data)
|
const message = ethUtil.stripHexPrefix(data)
|
||||||
var privKey = wallet.getPrivateKey()
|
var privKey = wallet.getPrivateKey()
|
||||||
var msgSig = ethUtil.ecsign(new Buffer(message, 'hex'), privKey)
|
var msgSig = ethUtil.ecsign(new Buffer(message, 'hex'), privKey)
|
||||||
var rawMsgSig = ethUtil.bufferToHex(sigUtil.concatSig(msgSig.v, msgSig.r, msgSig.s))
|
var rawMsgSig = ethUtil.bufferToHex(sigUtil.concatSig(msgSig.v, msgSig.r, msgSig.s))
|
||||||
|
@ -20,13 +20,19 @@ class SimpleKeyring extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
deserialize (privateKeys = []) {
|
deserialize (privateKeys = []) {
|
||||||
this.wallets = privateKeys.map((privateKey) => {
|
return new Promise((resolve, reject) => {
|
||||||
const stripped = ethUtil.stripHexPrefix(privateKey)
|
try {
|
||||||
const buffer = new Buffer(stripped, 'hex')
|
this.wallets = privateKeys.map((privateKey) => {
|
||||||
const wallet = Wallet.fromPrivateKey(buffer)
|
const stripped = ethUtil.stripHexPrefix(privateKey)
|
||||||
return wallet
|
const buffer = new Buffer(stripped, 'hex')
|
||||||
|
const wallet = Wallet.fromPrivateKey(buffer)
|
||||||
|
return wallet
|
||||||
|
})
|
||||||
|
} catch (e) {
|
||||||
|
reject(e)
|
||||||
|
}
|
||||||
|
resolve()
|
||||||
})
|
})
|
||||||
return Promise.resolve()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
addAccounts (n = 1) {
|
addAccounts (n = 1) {
|
||||||
@ -35,12 +41,12 @@ class SimpleKeyring extends EventEmitter {
|
|||||||
newWallets.push(Wallet.generate())
|
newWallets.push(Wallet.generate())
|
||||||
}
|
}
|
||||||
this.wallets = this.wallets.concat(newWallets)
|
this.wallets = this.wallets.concat(newWallets)
|
||||||
const hexWallets = newWallets.map(w => w.getAddress().toString('hex'))
|
const hexWallets = newWallets.map(w => ethUtil.bufferToHex(w.getAddress()))
|
||||||
return Promise.resolve(hexWallets)
|
return Promise.resolve(hexWallets)
|
||||||
}
|
}
|
||||||
|
|
||||||
getAccounts () {
|
getAccounts () {
|
||||||
return Promise.resolve(this.wallets.map(w => w.getAddress().toString('hex')))
|
return Promise.resolve(this.wallets.map(w => ethUtil.bufferToHex(w.getAddress())))
|
||||||
}
|
}
|
||||||
|
|
||||||
// tx is an instance of the ethereumjs-transaction class.
|
// tx is an instance of the ethereumjs-transaction class.
|
||||||
@ -54,7 +60,7 @@ class SimpleKeyring extends EventEmitter {
|
|||||||
// For eth_sign, we need to sign transactions:
|
// For eth_sign, we need to sign transactions:
|
||||||
signMessage (withAccount, data) {
|
signMessage (withAccount, data) {
|
||||||
const wallet = this._getWalletForAccount(withAccount)
|
const wallet = this._getWalletForAccount(withAccount)
|
||||||
const message = ethUtil.removeHexPrefix(data)
|
const message = ethUtil.stripHexPrefix(data)
|
||||||
var privKey = wallet.getPrivateKey()
|
var privKey = wallet.getPrivateKey()
|
||||||
var msgSig = ethUtil.ecsign(new Buffer(message, 'hex'), privKey)
|
var msgSig = ethUtil.ecsign(new Buffer(message, 'hex'), privKey)
|
||||||
var rawMsgSig = ethUtil.bufferToHex(sigUtil.concatSig(msgSig.v, msgSig.r, msgSig.s))
|
var rawMsgSig = ethUtil.bufferToHex(sigUtil.concatSig(msgSig.v, msgSig.r, msgSig.s))
|
||||||
@ -70,7 +76,9 @@ class SimpleKeyring extends EventEmitter {
|
|||||||
/* PRIVATE METHODS */
|
/* PRIVATE METHODS */
|
||||||
|
|
||||||
_getWalletForAccount (account) {
|
_getWalletForAccount (account) {
|
||||||
return this.wallets.find(w => w.getAddress().toString('hex') === account)
|
let wallet = this.wallets.find(w => ethUtil.bufferToHex(w.getAddress()) === account)
|
||||||
|
if (!wallet) throw new Error('Simple Keyring - Unable to find matching address.')
|
||||||
|
return wallet
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -281,7 +281,7 @@ ConfigManager.prototype.updateConversionRate = function () {
|
|||||||
this.setConversionPrice(parsedResponse.ticker.price)
|
this.setConversionPrice(parsedResponse.ticker.price)
|
||||||
this.setConversionDate(parsedResponse.timestamp)
|
this.setConversionDate(parsedResponse.timestamp)
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
console.error('Error in conversion.', err)
|
console.warn('MetaMask - Failed to query currency conversion.')
|
||||||
this.setConversionPrice(0)
|
this.setConversionPrice(0)
|
||||||
this.setConversionDate('N/A')
|
this.setConversionDate('N/A')
|
||||||
})
|
})
|
||||||
|
@ -43,7 +43,9 @@ EthereumStore.prototype.addAccount = function (address) {
|
|||||||
self._currentState.accounts[address] = {}
|
self._currentState.accounts[address] = {}
|
||||||
self._didUpdate()
|
self._didUpdate()
|
||||||
if (!self.currentBlockNumber) return
|
if (!self.currentBlockNumber) return
|
||||||
self._updateAccount(address, noop)
|
self._updateAccount(address, () => {
|
||||||
|
self._didUpdate()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
EthereumStore.prototype.removeAccount = function (address) {
|
EthereumStore.prototype.removeAccount = function (address) {
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
const async = require('async')
|
const async = require('async')
|
||||||
const EthQuery = require('eth-query')
|
const EthQuery = require('eth-query')
|
||||||
const ethUtil = require('ethereumjs-util')
|
const ethUtil = require('ethereumjs-util')
|
||||||
|
const Transaction = require('ethereumjs-tx')
|
||||||
|
const normalize = require('./sig-util').normalize
|
||||||
const BN = ethUtil.BN
|
const BN = ethUtil.BN
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -14,6 +16,7 @@ module.exports = class txProviderUtils {
|
|||||||
this.provider = provider
|
this.provider = provider
|
||||||
this.query = new EthQuery(provider)
|
this.query = new EthQuery(provider)
|
||||||
}
|
}
|
||||||
|
|
||||||
analyzeGasUsage (txData, cb) {
|
analyzeGasUsage (txData, cb) {
|
||||||
var self = this
|
var self = this
|
||||||
this.query.getBlockByNumber('latest', true, (err, block) => {
|
this.query.getBlockByNumber('latest', true, (err, block) => {
|
||||||
@ -71,4 +74,59 @@ module.exports = class txProviderUtils {
|
|||||||
const correct = bnGas.add(gasBuffer)
|
const correct = bnGas.add(gasBuffer)
|
||||||
return ethUtil.addHexPrefix(correct.toString(16))
|
return ethUtil.addHexPrefix(correct.toString(16))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fillInTxParams (txParams, cb) {
|
||||||
|
let fromAddress = txParams.from
|
||||||
|
let 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) {
|
||||||
|
if (err) return cb(err)
|
||||||
|
// write results to txParams obj
|
||||||
|
Object.assign(txParams, result)
|
||||||
|
cb()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// builds ethTx from txParams object
|
||||||
|
buildEthTxFromParams (txParams, gasMultiplier = 1) {
|
||||||
|
// apply gas multiplyer
|
||||||
|
let gasPrice = new BN(ethUtil.stripHexPrefix(txParams.gasPrice), 16)
|
||||||
|
// multiply and divide by 100 so as to add percision to integer mul
|
||||||
|
gasPrice = gasPrice.mul(new BN(gasMultiplier * 100, 10)).div(new BN(100, 10))
|
||||||
|
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.nonce = normalize(txParams.nonce)
|
||||||
|
// build ethTx
|
||||||
|
const ethTx = new Transaction(txParams)
|
||||||
|
return ethTx
|
||||||
|
}
|
||||||
|
|
||||||
|
publishTransaction (rawTx, cb) {
|
||||||
|
this.query.sendRawTransaction(rawTx, cb)
|
||||||
|
}
|
||||||
|
|
||||||
|
validateTxParams (txParams, cb) {
|
||||||
|
if (('value' in txParams) && txParams.value.indexOf('-') === 0) {
|
||||||
|
cb(new Error(`Invalid transaction value of ${txParams.value} not a positive number.`))
|
||||||
|
} else {
|
||||||
|
cb()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// util
|
||||||
|
|
||||||
|
function isUndef(value) {
|
||||||
|
return value === undefined
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,8 @@ const IdStoreMigrator = require('./lib/idStore-migrator')
|
|||||||
const ObservableStore = require('./lib/observable/')
|
const ObservableStore = require('./lib/observable/')
|
||||||
const HostStore = require('./lib/observable/host')
|
const HostStore = require('./lib/observable/host')
|
||||||
const synchronizeStore = require('./lib/observable/util/sync')
|
const synchronizeStore = require('./lib/observable/util/sync')
|
||||||
|
const accountImporter = require('./account-import-strategies')
|
||||||
|
|
||||||
const version = require('../manifest.json').version
|
const version = require('../manifest.json').version
|
||||||
|
|
||||||
module.exports = class MetamaskController extends EventEmitter {
|
module.exports = class MetamaskController extends EventEmitter {
|
||||||
@ -57,6 +59,7 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||||||
getSelectedAccount: this.configManager.getSelectedAccount.bind(this.configManager),
|
getSelectedAccount: this.configManager.getSelectedAccount.bind(this.configManager),
|
||||||
getGasMultiplier: this.configManager.getGasMultiplier.bind(this.configManager),
|
getGasMultiplier: this.configManager.getGasMultiplier.bind(this.configManager),
|
||||||
getNetwork: this.getStateNetwork.bind(this),
|
getNetwork: this.getStateNetwork.bind(this),
|
||||||
|
signTransaction: this.keyringController.signTransaction.bind(this.keyringController),
|
||||||
provider: this.provider,
|
provider: this.provider,
|
||||||
blockTracker: this.provider,
|
blockTracker: this.provider,
|
||||||
})
|
})
|
||||||
@ -77,6 +80,7 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||||||
|
|
||||||
this.ethStore.on('update', this.sendUpdate.bind(this))
|
this.ethStore.on('update', this.sendUpdate.bind(this))
|
||||||
this.keyringController.on('update', this.sendUpdate.bind(this))
|
this.keyringController.on('update', this.sendUpdate.bind(this))
|
||||||
|
this.txManager.on('update', this.sendUpdate.bind(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
getState () {
|
getState () {
|
||||||
@ -125,7 +129,22 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||||||
.then((newState) => { cb(null, newState) })
|
.then((newState) => { cb(null, newState) })
|
||||||
.catch((reason) => { cb(reason) })
|
.catch((reason) => { cb(reason) })
|
||||||
},
|
},
|
||||||
addNewKeyring: nodeify(keyringController.addNewKeyring).bind(keyringController),
|
addNewKeyring: (type, opts, cb) => {
|
||||||
|
keyringController.addNewKeyring(type, opts)
|
||||||
|
.then(() => keyringController.fullUpdate())
|
||||||
|
.then((newState) => { cb(null, newState) })
|
||||||
|
.catch((reason) => { cb(reason) })
|
||||||
|
},
|
||||||
|
importAccountWithStrategy: (strategy, args, cb) => {
|
||||||
|
accountImporter.importAccount(strategy, args)
|
||||||
|
.then((privateKey) => {
|
||||||
|
return keyringController.addNewKeyring('Simple Key Pair', [ privateKey ])
|
||||||
|
})
|
||||||
|
.then(keyring => keyring.getAccounts())
|
||||||
|
.then((accounts) => keyringController.setSelectedAccount(accounts[0]))
|
||||||
|
.then(() => { cb(null, keyringController.fullUpdate()) })
|
||||||
|
.catch((reason) => { cb(reason) })
|
||||||
|
},
|
||||||
addNewAccount: nodeify(keyringController.addNewAccount).bind(keyringController),
|
addNewAccount: nodeify(keyringController.addNewAccount).bind(keyringController),
|
||||||
setSelectedAccount: nodeify(keyringController.setSelectedAccount).bind(keyringController),
|
setSelectedAccount: nodeify(keyringController.setSelectedAccount).bind(keyringController),
|
||||||
saveAccountLabel: nodeify(keyringController.saveAccountLabel).bind(keyringController),
|
saveAccountLabel: nodeify(keyringController.saveAccountLabel).bind(keyringController),
|
||||||
@ -200,26 +219,7 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||||||
cb(null, result)
|
cb(null, result)
|
||||||
},
|
},
|
||||||
// tx signing
|
// tx signing
|
||||||
approveTransaction: this.newUnsignedTransaction.bind(this),
|
processTransaction: (txParams, cb) => this.newUnapprovedTransaction(txParams, cb),
|
||||||
signTransaction: (txParams, cb) => {
|
|
||||||
this.txManager.formatTxForSigining(txParams)
|
|
||||||
.then(({ethTx, address, txId}) => {
|
|
||||||
return this.keyringController.signTransaction(ethTx, address, txId)
|
|
||||||
})
|
|
||||||
.then(({tx, txId}) => {
|
|
||||||
return this.txManager.resolveSignedTransaction({tx, txId})
|
|
||||||
})
|
|
||||||
.then((rawTx) => {
|
|
||||||
cb(null, rawTx)
|
|
||||||
this.sendUpdate()
|
|
||||||
this.txManager.emit(`${txParams.metamaskId}:signingComplete`)
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
console.error(err)
|
|
||||||
cb(err)
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
// msg signing
|
// msg signing
|
||||||
approveMessage: this.newUnsignedMessage.bind(this),
|
approveMessage: this.newUnsignedMessage.bind(this),
|
||||||
signMessage: (...args) => {
|
signMessage: (...args) => {
|
||||||
@ -259,24 +259,26 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||||||
return publicConfigStore
|
return publicConfigStore
|
||||||
}
|
}
|
||||||
|
|
||||||
newUnsignedTransaction (txParams, onTxDoneCb) {
|
newUnapprovedTransaction (txParams, cb) {
|
||||||
const txManager = this.txManager
|
const self = this
|
||||||
const err = this.enforceTxValidations(txParams)
|
self.txManager.addUnapprovedTransaction(txParams, (err, txMeta) => {
|
||||||
if (err) return onTxDoneCb(err)
|
if (err) return cb(err)
|
||||||
txManager.addUnapprovedTransaction(txParams, onTxDoneCb, (err, txData) => {
|
self.sendUpdate()
|
||||||
if (err) return onTxDoneCb(err)
|
self.opts.showUnapprovedTx(txMeta)
|
||||||
this.sendUpdate()
|
// listen for tx completion (success, fail)
|
||||||
this.opts.showUnapprovedTx(txParams, txData, onTxDoneCb)
|
self.txManager.once(`${txMeta.id}:finished`, (status) => {
|
||||||
|
switch (status) {
|
||||||
|
case 'submitted':
|
||||||
|
return cb(null, txMeta.hash)
|
||||||
|
case 'rejected':
|
||||||
|
return cb(new Error('MetaMask Tx Signature: User denied transaction signature.'))
|
||||||
|
default:
|
||||||
|
return cb(new Error(`MetaMask Tx Signature: Unknown problem: ${JSON.stringify(txMeta.txParams)}`))
|
||||||
|
}
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
enforceTxValidations (txParams) {
|
|
||||||
if (('value' in txParams) && txParams.value.indexOf('-') === 0) {
|
|
||||||
const msg = `Invalid transaction value of ${txParams.value} not a positive number.`
|
|
||||||
return new Error(msg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
newUnsignedMessage (msgParams, cb) {
|
newUnsignedMessage (msgParams, cb) {
|
||||||
var state = this.keyringController.getState()
|
var state = this.keyringController.getState()
|
||||||
if (!state.isUnlocked) {
|
if (!state.isUnlocked) {
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
const EventEmitter = require('events')
|
const EventEmitter = require('events')
|
||||||
|
const async = require('async')
|
||||||
const extend = require('xtend')
|
const extend = require('xtend')
|
||||||
|
const Semaphore = require('semaphore')
|
||||||
const ethUtil = require('ethereumjs-util')
|
const ethUtil = require('ethereumjs-util')
|
||||||
const Transaction = require('ethereumjs-tx')
|
const BN = require('ethereumjs-util').BN
|
||||||
const BN = ethUtil.BN
|
|
||||||
const TxProviderUtil = require('./lib/tx-utils')
|
const TxProviderUtil = require('./lib/tx-utils')
|
||||||
const createId = require('./lib/random-id')
|
const createId = require('./lib/random-id')
|
||||||
const normalize = require('./lib/sig-util').normalize
|
|
||||||
|
|
||||||
module.exports = class TransactionManager extends EventEmitter {
|
module.exports = class TransactionManager extends EventEmitter {
|
||||||
constructor (opts) {
|
constructor (opts) {
|
||||||
@ -20,6 +20,8 @@ module.exports = class TransactionManager extends EventEmitter {
|
|||||||
this.blockTracker.on('block', this.checkForTxInBlock.bind(this))
|
this.blockTracker.on('block', this.checkForTxInBlock.bind(this))
|
||||||
this.getGasMultiplier = opts.getGasMultiplier
|
this.getGasMultiplier = opts.getGasMultiplier
|
||||||
this.getNetwork = opts.getNetwork
|
this.getNetwork = opts.getNetwork
|
||||||
|
this.signEthTx = opts.signTransaction
|
||||||
|
this.nonceLock = Semaphore(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
getState () {
|
getState () {
|
||||||
@ -33,11 +35,12 @@ module.exports = class TransactionManager extends EventEmitter {
|
|||||||
|
|
||||||
// Returns the tx list
|
// Returns the tx list
|
||||||
getTxList () {
|
getTxList () {
|
||||||
return this.txList
|
let network = this.getNetwork()
|
||||||
|
return this.txList.filter(txMeta => txMeta.metamaskNetworkId === network)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adds a tx to the txlist
|
// Adds a tx to the txlist
|
||||||
addTx (txMeta, onTxDoneCb = warn) {
|
addTx (txMeta) {
|
||||||
var txList = this.getTxList()
|
var txList = this.getTxList()
|
||||||
var txHistoryLimit = this.txHistoryLimit
|
var txHistoryLimit = this.txHistoryLimit
|
||||||
|
|
||||||
@ -53,16 +56,11 @@ module.exports = class TransactionManager extends EventEmitter {
|
|||||||
txList.push(txMeta)
|
txList.push(txMeta)
|
||||||
|
|
||||||
this._saveTxList(txList)
|
this._saveTxList(txList)
|
||||||
// keep the onTxDoneCb around in a listener
|
|
||||||
// for after approval/denial (requires user interaction)
|
|
||||||
// This onTxDoneCb fires completion to the Dapp's write operation.
|
|
||||||
this.once(`${txMeta.id}:signed`, function (txId) {
|
this.once(`${txMeta.id}:signed`, function (txId) {
|
||||||
this.removeAllListeners(`${txMeta.id}:rejected`)
|
this.removeAllListeners(`${txMeta.id}:rejected`)
|
||||||
onTxDoneCb(null, true)
|
|
||||||
})
|
})
|
||||||
this.once(`${txMeta.id}:rejected`, function (txId) {
|
this.once(`${txMeta.id}:rejected`, function (txId) {
|
||||||
this.removeAllListeners(`${txMeta.id}:signed`)
|
this.removeAllListeners(`${txMeta.id}:signed`)
|
||||||
onTxDoneCb(null, false)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
this.emit('updateBadge')
|
this.emit('updateBadge')
|
||||||
@ -83,6 +81,7 @@ module.exports = class TransactionManager extends EventEmitter {
|
|||||||
var index = txList.findIndex(txData => txData.id === txId)
|
var index = txList.findIndex(txData => txData.id === txId)
|
||||||
txList[index] = txMeta
|
txList[index] = txMeta
|
||||||
this._saveTxList(txList)
|
this._saveTxList(txList)
|
||||||
|
this.emit('update')
|
||||||
}
|
}
|
||||||
|
|
||||||
get unapprovedTxCount () {
|
get unapprovedTxCount () {
|
||||||
@ -93,28 +92,51 @@ module.exports = class TransactionManager extends EventEmitter {
|
|||||||
return this.getTxsByMetaData('status', 'signed').length
|
return this.getTxsByMetaData('status', 'signed').length
|
||||||
}
|
}
|
||||||
|
|
||||||
addUnapprovedTransaction (txParams, onTxDoneCb, cb) {
|
addUnapprovedTransaction (txParams, done) {
|
||||||
// create txData obj with parameters and meta data
|
let txMeta
|
||||||
var time = (new Date()).getTime()
|
async.waterfall([
|
||||||
var txId = createId()
|
// validate
|
||||||
txParams.metamaskId = txId
|
(cb) => this.txProviderUtils.validateTxParams(txParams, cb),
|
||||||
txParams.metamaskNetworkId = this.getNetwork()
|
// prepare txMeta
|
||||||
var txData = {
|
(cb) => {
|
||||||
id: txId,
|
// create txMeta obj with parameters and meta data
|
||||||
txParams: txParams,
|
let time = (new Date()).getTime()
|
||||||
time: time,
|
let txId = createId()
|
||||||
status: 'unapproved',
|
txParams.metamaskId = txId
|
||||||
gasMultiplier: this.getGasMultiplier() || 1,
|
txParams.metamaskNetworkId = this.getNetwork()
|
||||||
metamaskNetworkId: this.getNetwork(),
|
txMeta = {
|
||||||
}
|
id: txId,
|
||||||
this.txProviderUtils.analyzeGasUsage(txData, this.txDidComplete.bind(this, txData, onTxDoneCb, cb))
|
time: time,
|
||||||
// calculate metadata for tx
|
status: 'unapproved',
|
||||||
|
gasMultiplier: this.getGasMultiplier() || 1,
|
||||||
|
metamaskNetworkId: this.getNetwork(),
|
||||||
|
txParams: txParams,
|
||||||
|
}
|
||||||
|
// calculate metadata for tx
|
||||||
|
this.txProviderUtils.analyzeGasUsage(txMeta, cb)
|
||||||
|
},
|
||||||
|
// save txMeta
|
||||||
|
(cb) => {
|
||||||
|
this.addTx(txMeta)
|
||||||
|
this.setMaxTxCostAndFee(txMeta)
|
||||||
|
cb(null, txMeta)
|
||||||
|
},
|
||||||
|
], done)
|
||||||
}
|
}
|
||||||
|
|
||||||
txDidComplete (txMeta, onTxDoneCb, cb, err) {
|
setMaxTxCostAndFee (txMeta) {
|
||||||
if (err) return cb(err)
|
var txParams = txMeta.txParams
|
||||||
this.addTx(txMeta, onTxDoneCb)
|
var gasMultiplier = txMeta.gasMultiplier
|
||||||
cb(null, txMeta)
|
var gasCost = new BN(ethUtil.stripHexPrefix(txParams.gas || txMeta.estimatedGas), 16)
|
||||||
|
var gasPrice = new BN(ethUtil.stripHexPrefix(txParams.gasPrice || '0x4a817c800'), 16)
|
||||||
|
gasPrice = gasPrice.mul(new BN(gasMultiplier * 100), 10).div(new BN(100, 10))
|
||||||
|
var txFee = gasCost.mul(gasPrice)
|
||||||
|
var txValue = new BN(ethUtil.stripHexPrefix(txParams.value || '0x0'), 16)
|
||||||
|
var maxCost = txValue.add(txFee)
|
||||||
|
txMeta.txFee = txFee
|
||||||
|
txMeta.txValue = txValue
|
||||||
|
txMeta.maxCost = maxCost
|
||||||
|
this.updateTx(txMeta)
|
||||||
}
|
}
|
||||||
|
|
||||||
getUnapprovedTxList () {
|
getUnapprovedTxList () {
|
||||||
@ -127,8 +149,25 @@ module.exports = class TransactionManager extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
approveTransaction (txId, cb = warn) {
|
approveTransaction (txId, cb = warn) {
|
||||||
this.setTxStatusSigned(txId)
|
const self = this
|
||||||
this.once(`${txId}:signingComplete`, cb)
|
// approve
|
||||||
|
self.setTxStatusApproved(txId)
|
||||||
|
// only allow one tx at a time for atomic nonce usage
|
||||||
|
self.nonceLock.take(() => {
|
||||||
|
// begin signature process
|
||||||
|
async.waterfall([
|
||||||
|
(cb) => self.fillInTxParams(txId, cb),
|
||||||
|
(cb) => self.signTransaction(txId, cb),
|
||||||
|
(rawTx, cb) => self.publishTransaction(txId, rawTx, cb),
|
||||||
|
], (err) => {
|
||||||
|
self.nonceLock.leave()
|
||||||
|
if (err) {
|
||||||
|
this.setTxStatusFailed(txId)
|
||||||
|
return cb(err)
|
||||||
|
}
|
||||||
|
cb()
|
||||||
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
cancelTransaction (txId, cb = warn) {
|
cancelTransaction (txId, cb = warn) {
|
||||||
@ -136,38 +175,43 @@ module.exports = class TransactionManager extends EventEmitter {
|
|||||||
cb()
|
cb()
|
||||||
}
|
}
|
||||||
|
|
||||||
// formats txParams so the keyringController can sign it
|
fillInTxParams (txId, cb) {
|
||||||
formatTxForSigining (txParams) {
|
let txMeta = this.getTx(txId)
|
||||||
var address = txParams.from
|
this.txProviderUtils.fillInTxParams(txMeta.txParams, (err) => {
|
||||||
var metaTx = this.getTx(txParams.metamaskId)
|
if (err) return cb(err)
|
||||||
var gasMultiplier = metaTx.gasMultiplier
|
this.updateTx(txMeta)
|
||||||
var gasPrice = new BN(ethUtil.stripHexPrefix(txParams.gasPrice), 16)
|
cb()
|
||||||
gasPrice = gasPrice.mul(new BN(gasMultiplier * 100, 10)).div(new BN(100, 10))
|
})
|
||||||
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.nonce = normalize(txParams.nonce)
|
|
||||||
const ethTx = new Transaction(txParams)
|
|
||||||
var txId = txParams.metamaskId
|
|
||||||
return Promise.resolve({ethTx, address, txId})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// receives a signed tx object and updates the tx hash
|
signTransaction (txId, cb) {
|
||||||
// and pass it to the cb to be sent off
|
let txMeta = this.getTx(txId)
|
||||||
resolveSignedTransaction ({tx, txId, cb = warn}) {
|
let txParams = txMeta.txParams
|
||||||
// Add the tx hash to the persisted meta-tx object
|
let fromAddress = txParams.from
|
||||||
var txHash = ethUtil.bufferToHex(tx.hash())
|
let ethTx = this.txProviderUtils.buildEthTxFromParams(txParams, txMeta.gasMultiplier)
|
||||||
var metaTx = this.getTx(txId)
|
this.signEthTx(ethTx, fromAddress).then(() => {
|
||||||
metaTx.hash = txHash
|
this.setTxStatusSigned(txMeta.id)
|
||||||
this.updateTx(metaTx)
|
cb(null, ethUtil.bufferToHex(ethTx.serialize()))
|
||||||
var rawTx = ethUtil.bufferToHex(tx.serialize())
|
}).catch((err) => {
|
||||||
return Promise.resolve(rawTx)
|
cb(err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
publishTransaction (txId, rawTx, cb) {
|
||||||
|
this.txProviderUtils.publishTransaction(rawTx, (err, txHash) => {
|
||||||
|
if (err) return cb(err)
|
||||||
|
this.setTxHash(txId, txHash)
|
||||||
|
this.setTxStatusSubmitted(txId)
|
||||||
|
cb()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// receives a txHash records the tx as signed
|
||||||
|
setTxHash (txId, txHash) {
|
||||||
|
// Add the tx hash to the persisted meta-tx object
|
||||||
|
let txMeta = this.getTx(txId)
|
||||||
|
txMeta.hash = txHash
|
||||||
|
this.updateTx(txMeta)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -212,23 +256,35 @@ module.exports = class TransactionManager extends EventEmitter {
|
|||||||
return txMeta.status
|
return txMeta.status
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// should update the status of the tx to 'rejected'.
|
||||||
|
setTxStatusRejected (txId) {
|
||||||
|
this._setTxStatus(txId, 'rejected')
|
||||||
|
}
|
||||||
|
|
||||||
|
// should update the status of the tx to 'approved'.
|
||||||
|
setTxStatusApproved (txId) {
|
||||||
|
this._setTxStatus(txId, 'approved')
|
||||||
|
}
|
||||||
|
|
||||||
// should update the status of the tx to 'signed'.
|
// should update the status of the tx to 'signed'.
|
||||||
setTxStatusSigned (txId) {
|
setTxStatusSigned (txId) {
|
||||||
this._setTxStatus(txId, 'signed')
|
this._setTxStatus(txId, 'signed')
|
||||||
this.emit('updateBadge')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// should update the status of the tx to 'rejected'.
|
// should update the status of the tx to 'submitted'.
|
||||||
setTxStatusRejected (txId) {
|
setTxStatusSubmitted (txId) {
|
||||||
this._setTxStatus(txId, 'rejected')
|
this._setTxStatus(txId, 'submitted')
|
||||||
this.emit('updateBadge')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// should update the status of the tx to 'confirmed'.
|
||||||
setTxStatusConfirmed (txId) {
|
setTxStatusConfirmed (txId) {
|
||||||
this._setTxStatus(txId, 'confirmed')
|
this._setTxStatus(txId, 'confirmed')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setTxStatusFailed (txId) {
|
||||||
|
this._setTxStatus(txId, 'failed')
|
||||||
|
}
|
||||||
|
|
||||||
// merges txParams obj onto txData.txParams
|
// merges txParams obj onto txData.txParams
|
||||||
// use extend to ensure that all fields are filled
|
// use extend to ensure that all fields are filled
|
||||||
updateTxParams (txId, txParams) {
|
updateTxParams (txId, txParams) {
|
||||||
@ -240,19 +296,31 @@ module.exports = class TransactionManager extends EventEmitter {
|
|||||||
// checks if a signed tx is in a block and
|
// checks if a signed tx is in a block and
|
||||||
// if included sets the tx status as 'confirmed'
|
// if included sets the tx status as 'confirmed'
|
||||||
checkForTxInBlock () {
|
checkForTxInBlock () {
|
||||||
var signedTxList = this.getFilteredTxList({status: 'signed', err: undefined})
|
var signedTxList = this.getFilteredTxList({status: 'submitted'})
|
||||||
if (!signedTxList.length) return
|
if (!signedTxList.length) return
|
||||||
signedTxList.forEach((tx) => {
|
signedTxList.forEach((txMeta) => {
|
||||||
var txHash = tx.hash
|
var txHash = txMeta.hash
|
||||||
var txId = tx.id
|
var txId = txMeta.id
|
||||||
if (!txHash) return
|
if (!txHash) {
|
||||||
this.txProviderUtils.query.getTransactionByHash(txHash, (err, txMeta) => {
|
txMeta.err = {
|
||||||
if (err || !txMeta) {
|
errCode: 'No hash was provided',
|
||||||
tx.err = err || 'Tx could possibly have not been submitted'
|
message: 'We had an error while submitting this transaction, please try again.',
|
||||||
this.updateTx(tx)
|
|
||||||
return txMeta ? console.error(err) : console.debug(`txMeta is ${txMeta} for:`, tx)
|
|
||||||
}
|
}
|
||||||
if (txMeta.blockNumber) {
|
this.updateTx(txMeta)
|
||||||
|
return this.setTxStatusFailed(txId)
|
||||||
|
}
|
||||||
|
this.txProviderUtils.query.getTransactionByHash(txHash, (err, txParams) => {
|
||||||
|
if (err || !txParams) {
|
||||||
|
if (!txParams) return
|
||||||
|
txMeta.err = {
|
||||||
|
isWarning: true,
|
||||||
|
errorCode: err,
|
||||||
|
message: 'There was a problem loading this transaction.',
|
||||||
|
}
|
||||||
|
this.updateTx(txMeta)
|
||||||
|
return console.error(err)
|
||||||
|
}
|
||||||
|
if (txParams.blockNumber) {
|
||||||
this.setTxStatusConfirmed(txId)
|
this.setTxStatusConfirmed(txId)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -266,6 +334,7 @@ module.exports = class TransactionManager extends EventEmitter {
|
|||||||
// should set the status in txData
|
// should set the status in txData
|
||||||
// - `'unapproved'` the user has not responded
|
// - `'unapproved'` the user has not responded
|
||||||
// - `'rejected'` the user has responded no!
|
// - `'rejected'` the user has responded no!
|
||||||
|
// - `'approved'` the user has approved the tx
|
||||||
// - `'signed'` the tx is signed
|
// - `'signed'` the tx is signed
|
||||||
// - `'submitted'` the tx is sent to a server
|
// - `'submitted'` the tx is sent to a server
|
||||||
// - `'confirmed'` the tx has been included in a block.
|
// - `'confirmed'` the tx has been included in a block.
|
||||||
@ -273,7 +342,11 @@ module.exports = class TransactionManager extends EventEmitter {
|
|||||||
var txMeta = this.getTx(txId)
|
var txMeta = this.getTx(txId)
|
||||||
txMeta.status = status
|
txMeta.status = status
|
||||||
this.emit(`${txMeta.id}:${status}`, txId)
|
this.emit(`${txMeta.id}:${status}`, txId)
|
||||||
|
if (status === 'submitted' || status === 'rejected') {
|
||||||
|
this.emit(`${txMeta.id}:finished`, status)
|
||||||
|
}
|
||||||
this.updateTx(txMeta)
|
this.updateTx(txMeta)
|
||||||
|
this.emit('updateBadge')
|
||||||
}
|
}
|
||||||
|
|
||||||
// Saves the new/updated txList.
|
// Saves the new/updated txList.
|
||||||
|
84
development/states/account-list-with-imported.json
Normal file
84
development/states/account-list-with-imported.json
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
{
|
||||||
|
"metamask": {
|
||||||
|
"isInitialized": true,
|
||||||
|
"isUnlocked": true,
|
||||||
|
"rpcTarget": "https://rawtestrpc.metamask.io/",
|
||||||
|
"identities": {
|
||||||
|
"0x58bda1f9d87dc7d2bcc6f7c2513efc9d03fca683": {
|
||||||
|
"address": "0x58bda1f9d87dc7d2bcc6f7c2513efc9d03fca683",
|
||||||
|
"name": "Account 1"
|
||||||
|
},
|
||||||
|
"0x9858e7d8b79fc3e6d989636721584498926da38a": {
|
||||||
|
"address": "0x9858e7d8b79fc3e6d989636721584498926da38a",
|
||||||
|
"name": "Imported Account"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"unconfTxs": {},
|
||||||
|
"currentFiat": "USD",
|
||||||
|
"conversionRate": 10.19458075,
|
||||||
|
"conversionDate": 1484696373,
|
||||||
|
"noActiveNotices": true,
|
||||||
|
"network": "3",
|
||||||
|
"accounts": {
|
||||||
|
"0x58bda1f9d87dc7d2bcc6f7c2513efc9d03fca683": {
|
||||||
|
"code": "0x",
|
||||||
|
"balance": "0x0",
|
||||||
|
"nonce": "0x0",
|
||||||
|
"address": "0x58bda1f9d87dc7d2bcc6f7c2513efc9d03fca683"
|
||||||
|
},
|
||||||
|
"0x9858e7d8b79fc3e6d989636721584498926da38a": {
|
||||||
|
"code": "0x",
|
||||||
|
"balance": "0x0",
|
||||||
|
"nonce": "0x0",
|
||||||
|
"address": "0x9858e7d8b79fc3e6d989636721584498926da38a"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"transactions": [],
|
||||||
|
"provider": {
|
||||||
|
"type": "testnet"
|
||||||
|
},
|
||||||
|
"selectedAccount": "0x9858e7d8b79fc3e6d989636721584498926da38a",
|
||||||
|
"selectedAccountTxList": [],
|
||||||
|
"isDisclaimerConfirmed": true,
|
||||||
|
"unconfMsgs": {},
|
||||||
|
"messages": [],
|
||||||
|
"shapeShiftTxList": [],
|
||||||
|
"keyringTypes": [
|
||||||
|
"Simple Key Pair",
|
||||||
|
"HD Key Tree"
|
||||||
|
],
|
||||||
|
"keyrings": [
|
||||||
|
{
|
||||||
|
"type": "HD Key Tree",
|
||||||
|
"accounts": [
|
||||||
|
"58bda1f9d87dc7d2bcc6f7c2513efc9d03fca683"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "Simple Key Pair",
|
||||||
|
"accounts": [
|
||||||
|
"0x9858e7d8b79fc3e6d989636721584498926da38a"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"lostAccounts": [],
|
||||||
|
"seedWords": null
|
||||||
|
},
|
||||||
|
"appState": {
|
||||||
|
"menuOpen": false,
|
||||||
|
"currentView": {
|
||||||
|
"name": "accounts"
|
||||||
|
},
|
||||||
|
"accountDetail": {
|
||||||
|
"subview": "transactions",
|
||||||
|
"accountExport": "none",
|
||||||
|
"privateKey": ""
|
||||||
|
},
|
||||||
|
"transForward": true,
|
||||||
|
"isLoading": false,
|
||||||
|
"warning": null,
|
||||||
|
"scrollToBottom": false,
|
||||||
|
"forgottenPassword": false
|
||||||
|
},
|
||||||
|
"identities": {}
|
||||||
|
}
|
124
development/states/compilation-bug.json
Normal file
124
development/states/compilation-bug.json
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
{
|
||||||
|
"metamask": {
|
||||||
|
"isInitialized": true,
|
||||||
|
"isUnlocked": true,
|
||||||
|
"rpcTarget": "https://rawtestrpc.metamask.io/",
|
||||||
|
"identities": {
|
||||||
|
"0xac39b311dceb2a4b2f5d8461c1cdaf756f4f7ae9": {
|
||||||
|
"address": "0xac39b311dceb2a4b2f5d8461c1cdaf756f4f7ae9",
|
||||||
|
"name": "Account 1"
|
||||||
|
},
|
||||||
|
"0xd7c0cd9e7d2701c710d64fc492c7086679bdf7b4": {
|
||||||
|
"address": "0xd7c0cd9e7d2701c710d64fc492c7086679bdf7b4",
|
||||||
|
"name": "Account 2"
|
||||||
|
},
|
||||||
|
"0x1acfb961c5a8268eac8e09d6241a26cbeff42241": {
|
||||||
|
"address": "0x1acfb961c5a8268eac8e09d6241a26cbeff42241",
|
||||||
|
"name": "Account 3"
|
||||||
|
},
|
||||||
|
"0xabc2bca51709b8615147352c62420f547a63a00c": {
|
||||||
|
"address": "0xabc2bca51709b8615147352c62420f547a63a00c",
|
||||||
|
"name": "Account 4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"unconfTxs": {
|
||||||
|
"7992944905869041": {
|
||||||
|
"id": 7992944905869041,
|
||||||
|
"txParams": {
|
||||||
|
"from": "0xac39b311dceb2a4b2f5d8461c1cdaf756f4f7ae9",
|
||||||
|
"value": "0x0",
|
||||||
|
"data": "0x606060405234610000575b60da806100186000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680630dbe671f14603c575b6000565b3460005760466088565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b600060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16815600a165627a7a72305820a99dfa6091771f518dd1ae8d1ee347bae3304dffd98fd24b1b99a8380bc60a750029",
|
||||||
|
"gas": "0x1af75",
|
||||||
|
"metamaskId": 7992944905869041,
|
||||||
|
"metamaskNetworkId": "3"
|
||||||
|
},
|
||||||
|
"time": 1482279685589,
|
||||||
|
"status": "unconfirmed",
|
||||||
|
"gasMultiplier": 1,
|
||||||
|
"metamaskNetworkId": "3",
|
||||||
|
"gasLimitSpecified": true,
|
||||||
|
"estimatedGas": "0x1af75",
|
||||||
|
"simulationFails": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"currentFiat": "USD",
|
||||||
|
"conversionRate": 7.69158136,
|
||||||
|
"conversionDate": 1482279663,
|
||||||
|
"noActiveNotices": true,
|
||||||
|
"network": "3",
|
||||||
|
"accounts": {
|
||||||
|
"0xac39b311dceb2a4b2f5d8461c1cdaf756f4f7ae9": {
|
||||||
|
"code": "0x",
|
||||||
|
"nonce": "0x3",
|
||||||
|
"balance": "0x11f646fe14c9c000",
|
||||||
|
"address": "0xac39b311dceb2a4b2f5d8461c1cdaf756f4f7ae9"
|
||||||
|
},
|
||||||
|
"0xd7c0cd9e7d2701c710d64fc492c7086679bdf7b4": {
|
||||||
|
"code": "0x",
|
||||||
|
"nonce": "0x0",
|
||||||
|
"balance": "0x0",
|
||||||
|
"address": "0xd7c0cd9e7d2701c710d64fc492c7086679bdf7b4"
|
||||||
|
},
|
||||||
|
"0x1acfb961c5a8268eac8e09d6241a26cbeff42241": {
|
||||||
|
"code": "0x",
|
||||||
|
"balance": "0x0",
|
||||||
|
"nonce": "0x0",
|
||||||
|
"address": "0x1acfb961c5a8268eac8e09d6241a26cbeff42241"
|
||||||
|
},
|
||||||
|
"0xabc2bca51709b8615147352c62420f547a63a00c": {
|
||||||
|
"code": "0x",
|
||||||
|
"balance": "0x0",
|
||||||
|
"nonce": "0x0",
|
||||||
|
"address": "0xabc2bca51709b8615147352c62420f547a63a00c"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"transactions": [
|
||||||
|
{
|
||||||
|
"id": 7992944905869041,
|
||||||
|
"txParams": {
|
||||||
|
"from": "0xac39b311dceb2a4b2f5d8461c1cdaf756f4f7ae9",
|
||||||
|
"value": "0x0",
|
||||||
|
"data": "0x606060405234610000575b60da806100186000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680630dbe671f14603c575b6000565b3460005760466088565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b600060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16815600a165627a7a72305820a99dfa6091771f518dd1ae8d1ee347bae3304dffd98fd24b1b99a8380bc60a750029",
|
||||||
|
"gas": "0x1af75",
|
||||||
|
"metamaskId": 7992944905869041,
|
||||||
|
"metamaskNetworkId": "3"
|
||||||
|
},
|
||||||
|
"time": 1482279685589,
|
||||||
|
"status": "unconfirmed",
|
||||||
|
"gasMultiplier": 1,
|
||||||
|
"metamaskNetworkId": "3",
|
||||||
|
"gasLimitSpecified": true,
|
||||||
|
"estimatedGas": "0x1af75",
|
||||||
|
"simulationFails": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"provider": {
|
||||||
|
"type": "testnet"
|
||||||
|
},
|
||||||
|
"selectedAccount": "0xac39b311dceb2a4b2f5d8461c1cdaf756f4f7ae9",
|
||||||
|
"seedWords": false,
|
||||||
|
"isDisclaimerConfirmed": true,
|
||||||
|
"unconfMsgs": {},
|
||||||
|
"messages": [],
|
||||||
|
"shapeShiftTxList": [],
|
||||||
|
"keyringTypes": [
|
||||||
|
"Simple Key Pair",
|
||||||
|
"HD Key Tree"
|
||||||
|
],
|
||||||
|
"lostAccounts": []
|
||||||
|
},
|
||||||
|
"appState": {
|
||||||
|
"menuOpen": false,
|
||||||
|
"currentView": {
|
||||||
|
"name": "confTx",
|
||||||
|
"context": 0
|
||||||
|
},
|
||||||
|
"accountDetail": {
|
||||||
|
"subview": "transactions"
|
||||||
|
},
|
||||||
|
"transForward": true,
|
||||||
|
"isLoading": false,
|
||||||
|
"warning": null
|
||||||
|
},
|
||||||
|
"identities": {}
|
||||||
|
}
|
92
development/states/import-private-key-warning.json
Normal file
92
development/states/import-private-key-warning.json
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
{
|
||||||
|
"metamask": {
|
||||||
|
"isInitialized": true,
|
||||||
|
"isUnlocked": true,
|
||||||
|
"rpcTarget": "https://rawtestrpc.metamask.io/",
|
||||||
|
"identities": {
|
||||||
|
"0x01208723ba84e15da2e71656544a2963b0c06d40": {
|
||||||
|
"address": "0x01208723ba84e15da2e71656544a2963b0c06d40",
|
||||||
|
"name": "Account 1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"unconfTxs": {},
|
||||||
|
"currentFiat": "USD",
|
||||||
|
"conversionRate": 10.1219126,
|
||||||
|
"conversionDate": 1484695442,
|
||||||
|
"noActiveNotices": true,
|
||||||
|
"network": "3",
|
||||||
|
"accounts": {
|
||||||
|
"0x01208723ba84e15da2e71656544a2963b0c06d40": {
|
||||||
|
"nonce": "0x0",
|
||||||
|
"balance": "0x0",
|
||||||
|
"code": "0x",
|
||||||
|
"address": "0x01208723ba84e15da2e71656544a2963b0c06d40"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"transactions": [],
|
||||||
|
"provider": {
|
||||||
|
"type": "testnet"
|
||||||
|
},
|
||||||
|
"selectedAccount": "0x01208723ba84e15da2e71656544a2963b0c06d40",
|
||||||
|
"selectedAccountTxList": [],
|
||||||
|
"seedWords": false,
|
||||||
|
"isDisclaimerConfirmed": true,
|
||||||
|
"unconfMsgs": {},
|
||||||
|
"messages": [],
|
||||||
|
"shapeShiftTxList": [],
|
||||||
|
"keyringTypes": [
|
||||||
|
"Simple Key Pair",
|
||||||
|
"HD Key Tree"
|
||||||
|
],
|
||||||
|
"keyrings": [
|
||||||
|
{
|
||||||
|
"type": "Simple Key Pair",
|
||||||
|
"accounts": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "Simple Key Pair",
|
||||||
|
"accounts": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "Simple Key Pair",
|
||||||
|
"accounts": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "Simple Key Pair",
|
||||||
|
"accounts": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "Simple Key Pair",
|
||||||
|
"accounts": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "Simple Key Pair",
|
||||||
|
"accounts": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "Simple Key Pair",
|
||||||
|
"accounts": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "HD Key Tree",
|
||||||
|
"accounts": [
|
||||||
|
"01208723ba84e15da2e71656544a2963b0c06d40"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"lostAccounts": []
|
||||||
|
},
|
||||||
|
"appState": {
|
||||||
|
"menuOpen": false,
|
||||||
|
"currentView": {
|
||||||
|
"name": "import-menu"
|
||||||
|
},
|
||||||
|
"accountDetail": {
|
||||||
|
"subview": "transactions"
|
||||||
|
},
|
||||||
|
"transForward": true,
|
||||||
|
"isLoading": false,
|
||||||
|
"warning": "Invalid hex string"
|
||||||
|
},
|
||||||
|
"identities": {}
|
||||||
|
}
|
64
development/states/import-private-key.json
Normal file
64
development/states/import-private-key.json
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
{
|
||||||
|
"metamask": {
|
||||||
|
"isInitialized": true,
|
||||||
|
"isUnlocked": true,
|
||||||
|
"rpcTarget": "https://rawtestrpc.metamask.io/",
|
||||||
|
"identities": {
|
||||||
|
"0x01208723ba84e15da2e71656544a2963b0c06d40": {
|
||||||
|
"address": "0x01208723ba84e15da2e71656544a2963b0c06d40",
|
||||||
|
"name": "Account 1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"unconfTxs": {},
|
||||||
|
"currentFiat": "USD",
|
||||||
|
"conversionRate": 10.10788584,
|
||||||
|
"conversionDate": 1484694362,
|
||||||
|
"noActiveNotices": true,
|
||||||
|
"network": "3",
|
||||||
|
"accounts": {
|
||||||
|
"0x01208723ba84e15da2e71656544a2963b0c06d40": {
|
||||||
|
"balance": "0x0",
|
||||||
|
"code": "0x",
|
||||||
|
"nonce": "0x0",
|
||||||
|
"address": "0x01208723ba84e15da2e71656544a2963b0c06d40"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"transactions": [],
|
||||||
|
"provider": {
|
||||||
|
"type": "testnet"
|
||||||
|
},
|
||||||
|
"selectedAccount": "0x01208723ba84e15da2e71656544a2963b0c06d40",
|
||||||
|
"selectedAccountTxList": [],
|
||||||
|
"seedWords": null,
|
||||||
|
"isDisclaimerConfirmed": true,
|
||||||
|
"unconfMsgs": {},
|
||||||
|
"messages": [],
|
||||||
|
"shapeShiftTxList": [],
|
||||||
|
"keyringTypes": [
|
||||||
|
"Simple Key Pair",
|
||||||
|
"HD Key Tree"
|
||||||
|
],
|
||||||
|
"keyrings": [
|
||||||
|
{
|
||||||
|
"type": "HD Key Tree",
|
||||||
|
"accounts": [
|
||||||
|
"01208723ba84e15da2e71656544a2963b0c06d40"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"lostAccounts": []
|
||||||
|
},
|
||||||
|
"appState": {
|
||||||
|
"menuOpen": false,
|
||||||
|
"currentView": {
|
||||||
|
"name": "import-menu"
|
||||||
|
},
|
||||||
|
"accountDetail": {
|
||||||
|
"subview": "transactions"
|
||||||
|
},
|
||||||
|
"transForward": true,
|
||||||
|
"isLoading": false,
|
||||||
|
"warning": null
|
||||||
|
},
|
||||||
|
"identities": {}
|
||||||
|
}
|
66
development/states/new-account.json
Normal file
66
development/states/new-account.json
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
{
|
||||||
|
"metamask": {
|
||||||
|
"isInitialized": true,
|
||||||
|
"isUnlocked": true,
|
||||||
|
"rpcTarget": "https://rawtestrpc.metamask.io/",
|
||||||
|
"identities": {
|
||||||
|
"0xa6ef573d60594731178b7f85d80da13cc2af52dd": {
|
||||||
|
"address": "0xa6ef573d60594731178b7f85d80da13cc2af52dd",
|
||||||
|
"name": "Dan! 1"
|
||||||
|
},
|
||||||
|
"0xf9f52e84ad2c9122caa87478d27041ddaa215666": {
|
||||||
|
"address": "0xf9f52e84ad2c9122caa87478d27041ddaa215666",
|
||||||
|
"name": "Account 2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"unconfTxs": {},
|
||||||
|
"currentFiat": "USD",
|
||||||
|
"conversionRate": 10.92067835,
|
||||||
|
"conversionDate": 1478282884,
|
||||||
|
"network": null,
|
||||||
|
"accounts": {
|
||||||
|
"0xa6ef573d60594731178b7f85d80da13cc2af52dd": {
|
||||||
|
"balance": "0x00",
|
||||||
|
"nonce": "0x100000",
|
||||||
|
"code": "0x",
|
||||||
|
"address": "0xa6ef573d60594731178b7f85d80da13cc2af52dd"
|
||||||
|
},
|
||||||
|
"0xf9f52e84ad2c9122caa87478d27041ddaa215666": {
|
||||||
|
"balance": "0x00",
|
||||||
|
"nonce": "0x100000",
|
||||||
|
"code": "0x",
|
||||||
|
"address": "0xf9f52e84ad2c9122caa87478d27041ddaa215666"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"transactions": [],
|
||||||
|
"provider": {
|
||||||
|
"type": "testnet"
|
||||||
|
},
|
||||||
|
"selectedAccount": "0xa6ef573d60594731178b7f85d80da13cc2af52dd",
|
||||||
|
"isConfirmed": true,
|
||||||
|
"unconfMsgs": {},
|
||||||
|
"messages": [],
|
||||||
|
"selectedAddress": "0xa6ef573d60594731178b7f85d80da13cc2af52dd",
|
||||||
|
"shapeShiftTxList": [],
|
||||||
|
"keyringTypes": [
|
||||||
|
"Simple Key Pair",
|
||||||
|
"HD Key Tree"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"appState": {
|
||||||
|
"menuOpen": false,
|
||||||
|
"currentView": {
|
||||||
|
"name": "new-account"
|
||||||
|
},
|
||||||
|
"accountDetail": {
|
||||||
|
"subview": "transactions"
|
||||||
|
},
|
||||||
|
"transForward": true,
|
||||||
|
"isLoading": false,
|
||||||
|
"warning": null,
|
||||||
|
"forgottenPassword": null,
|
||||||
|
"detailView": {},
|
||||||
|
"scrollToBottom": false
|
||||||
|
},
|
||||||
|
"identities": {}
|
||||||
|
}
|
@ -1,12 +0,0 @@
|
|||||||
Due to [recent events](https://blog.ethereum.org/2016/11/20/from-morden-to-ropsten/), MetaMask is now deprecating support for the Morden Test Network.
|
|
||||||
|
|
||||||
Users will still be able to access Morden through a locally hosted node, but we will no longer be providing hosted access to this network through [Infura](http://infura.io/).
|
|
||||||
|
|
||||||
Please use the new Ropsten Network as your new default test network.
|
|
||||||
|
|
||||||
You can fund your Ropsten account using the buy button on your account page.
|
|
||||||
|
|
||||||
Best wishes!
|
|
||||||
|
|
||||||
The MetaMask Team
|
|
||||||
|
|
@ -84,19 +84,22 @@
|
|||||||
"react-hyperscript": "^2.2.2",
|
"react-hyperscript": "^2.2.2",
|
||||||
"react-markdown": "^2.3.0",
|
"react-markdown": "^2.3.0",
|
||||||
"react-redux": "^4.4.5",
|
"react-redux": "^4.4.5",
|
||||||
|
"react-select": "^1.0.0-rc.2",
|
||||||
|
"react-simple-file-input": "^1.0.0",
|
||||||
"react-tooltip-component": "^0.3.0",
|
"react-tooltip-component": "^0.3.0",
|
||||||
"readable-stream": "^2.1.2",
|
"readable-stream": "^2.1.2",
|
||||||
"redux": "^3.0.5",
|
"redux": "^3.0.5",
|
||||||
"redux-logger": "^2.3.1",
|
"redux-logger": "^2.3.1",
|
||||||
"redux-thunk": "^1.0.2",
|
"redux-thunk": "^1.0.2",
|
||||||
"sandwich-expando": "^1.0.5",
|
"sandwich-expando": "^1.0.5",
|
||||||
|
"semaphore": "^1.0.5",
|
||||||
"textarea-caret": "^3.0.1",
|
"textarea-caret": "^3.0.1",
|
||||||
"three.js": "^0.73.2",
|
"three.js": "^0.73.2",
|
||||||
"through2": "^2.0.1",
|
"through2": "^2.0.1",
|
||||||
"valid-url": "^1.0.9",
|
"valid-url": "^1.0.9",
|
||||||
"vreme": "^3.0.2",
|
"vreme": "^3.0.2",
|
||||||
"web3": "0.17.0-beta",
|
"web3": "0.17.0-beta",
|
||||||
"web3-provider-engine": "^8.2.0",
|
"web3-provider-engine": "^8.4.0",
|
||||||
"web3-stream-provider": "^2.0.6",
|
"web3-stream-provider": "^2.0.6",
|
||||||
"xtend": "^4.0.1"
|
"xtend": "^4.0.1"
|
||||||
},
|
},
|
||||||
|
@ -66,7 +66,8 @@ QUnit.test('agree to terms', function (assert) {
|
|||||||
}).then(function() {
|
}).then(function() {
|
||||||
|
|
||||||
var sandwich = app.find('.menu-droppo')[0]
|
var sandwich = app.find('.menu-droppo')[0]
|
||||||
var lock = sandwich.children[2]
|
var children = sandwich.children
|
||||||
|
var lock = children[children.length - 2]
|
||||||
assert.ok(lock, 'Lock menu item found')
|
assert.ok(lock, 'Lock menu item found')
|
||||||
lock.click()
|
lock.click()
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ var linkGen = require('../../ui/lib/explorer-link')
|
|||||||
describe('explorer-link', function() {
|
describe('explorer-link', function() {
|
||||||
|
|
||||||
it('adds testnet prefix to morden test network', function() {
|
it('adds testnet prefix to morden test network', function() {
|
||||||
var result = linkGen('hash', '2')
|
var result = linkGen('hash', '3')
|
||||||
assert.notEqual(result.indexOf('testnet'), -1, 'testnet injected')
|
assert.notEqual(result.indexOf('testnet'), -1, 'testnet injected')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
const assert = require('assert')
|
const assert = require('assert')
|
||||||
const extend = require('xtend')
|
const extend = require('xtend')
|
||||||
|
const ethUtil = require('ethereumjs-util')
|
||||||
const SimpleKeyring = require('../../../app/scripts/keyrings/simple')
|
const SimpleKeyring = require('../../../app/scripts/keyrings/simple')
|
||||||
const TYPE_STR = 'Simple Key Pair'
|
const TYPE_STR = 'Simple Key Pair'
|
||||||
|
|
||||||
@ -48,6 +49,24 @@ describe('simple-keyring', function() {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('#signMessage', function() {
|
||||||
|
const address = '0x9858e7d8b79fc3e6d989636721584498926da38a'
|
||||||
|
const message = '0x879a053d4800c6354e76c7985a865d2922c82fb5b3f4577b2fe08b998954f2e0'
|
||||||
|
const privateKey = '0x7dd98753d7b4394095de7d176c58128e2ed6ee600abe97c9f6d9fd65015d9b18'
|
||||||
|
const expectedResult = '0x28fcb6768e5110144a55b2e6ce9d1ea5a58103033632d272d2b5cf506906f7941a00b539383fd872109633d8c71c404e13dba87bc84166ee31b0e36061a69e161c'
|
||||||
|
|
||||||
|
it('passes the dennis test', function(done) {
|
||||||
|
keyring.deserialize([ privateKey ])
|
||||||
|
.then(() => {
|
||||||
|
return keyring.signMessage(address, message)
|
||||||
|
})
|
||||||
|
.then((result) => {
|
||||||
|
assert.equal(result, expectedResult)
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
describe('#addAccounts', function() {
|
describe('#addAccounts', function() {
|
||||||
describe('with no arguments', function() {
|
describe('with no arguments', function() {
|
||||||
it('creates a single wallet', function() {
|
it('creates a single wallet', function() {
|
||||||
@ -72,14 +91,10 @@ describe('simple-keyring', function() {
|
|||||||
it('calls getAddress on each wallet', function(done) {
|
it('calls getAddress on each wallet', function(done) {
|
||||||
|
|
||||||
// Push a mock wallet
|
// Push a mock wallet
|
||||||
const desiredOutput = 'foo'
|
const desiredOutput = '0x18a3462427bcc9133bb46e88bcbe39cd7ef0e761'
|
||||||
keyring.wallets.push({
|
keyring.wallets.push({
|
||||||
getAddress() {
|
getAddress() {
|
||||||
return {
|
return ethUtil.toBuffer(desiredOutput)
|
||||||
toString() {
|
|
||||||
return desiredOutput
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -27,24 +27,6 @@ describe('MetaMaskController', function() {
|
|||||||
this.sinon.restore()
|
this.sinon.restore()
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('#enforceTxValidations', function () {
|
|
||||||
it('returns null for positive values', function() {
|
|
||||||
var sample = {
|
|
||||||
value: '0x01'
|
|
||||||
}
|
|
||||||
var res = controller.enforceTxValidations(sample)
|
|
||||||
assert.equal(res, null, 'no error')
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
it('returns error for negative values', function() {
|
|
||||||
var sample = {
|
|
||||||
value: '-0x01'
|
|
||||||
}
|
|
||||||
var res = controller.enforceTxValidations(sample)
|
|
||||||
assert.ok(res, 'error')
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
@ -5,13 +5,14 @@ const nock = require('nock')
|
|||||||
const configManagerGen = require('../lib/mock-config-manager')
|
const configManagerGen = require('../lib/mock-config-manager')
|
||||||
const NoticeController = require('../../app/scripts/notice-controller')
|
const NoticeController = require('../../app/scripts/notice-controller')
|
||||||
const STORAGE_KEY = 'metamask-persistance-key'
|
const STORAGE_KEY = 'metamask-persistance-key'
|
||||||
// Hacking localStorage support into JSDom
|
|
||||||
window.localStorage = {}
|
|
||||||
|
|
||||||
describe('notice-controller', function() {
|
describe('notice-controller', function() {
|
||||||
var noticeController
|
var noticeController
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
|
// simple localStorage polyfill
|
||||||
|
window.localStorage = {}
|
||||||
|
if (window.localStorage.clear) window.localStorage.clear()
|
||||||
let configManager = configManagerGen()
|
let configManager = configManagerGen()
|
||||||
noticeController = new NoticeController({
|
noticeController = new NoticeController({
|
||||||
configManager: configManager,
|
configManager: configManager,
|
||||||
|
@ -15,6 +15,28 @@ describe('Transaction Manager', function() {
|
|||||||
provider: "testnet",
|
provider: "testnet",
|
||||||
txHistoryLimit: 10,
|
txHistoryLimit: 10,
|
||||||
blockTracker: new EventEmitter(),
|
blockTracker: new EventEmitter(),
|
||||||
|
getNetwork: function(){ return 'unit test' }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('#validateTxParams', function () {
|
||||||
|
it('returns null for positive values', function() {
|
||||||
|
var sample = {
|
||||||
|
value: '0x01'
|
||||||
|
}
|
||||||
|
var res = txManager.txProviderUtils.validateTxParams(sample, (err) => {
|
||||||
|
assert.equal(err, null, 'no error')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
it('returns error for negative values', function() {
|
||||||
|
var sample = {
|
||||||
|
value: '-0x01'
|
||||||
|
}
|
||||||
|
var res = txManager.txProviderUtils.validateTxParams(sample, (err) => {
|
||||||
|
assert.ok(err, 'error')
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -31,7 +53,7 @@ describe('Transaction Manager', function() {
|
|||||||
|
|
||||||
describe('#_saveTxList', function() {
|
describe('#_saveTxList', function() {
|
||||||
it('saves the submitted data to the tx list', function() {
|
it('saves the submitted data to the tx list', function() {
|
||||||
var target = [{ foo: 'bar' }]
|
var target = [{ foo: 'bar', metamaskNetworkId: 'unit test' }]
|
||||||
txManager._saveTxList(target)
|
txManager._saveTxList(target)
|
||||||
var result = txManager.getTxList()
|
var result = txManager.getTxList()
|
||||||
assert.equal(result[0].foo, 'bar')
|
assert.equal(result[0].foo, 'bar')
|
||||||
@ -40,7 +62,7 @@ describe('Transaction Manager', function() {
|
|||||||
|
|
||||||
describe('#addTx', function() {
|
describe('#addTx', function() {
|
||||||
it('adds a tx returned in getTxList', function() {
|
it('adds a tx returned in getTxList', function() {
|
||||||
var tx = { id: 1, status: 'confirmed',}
|
var tx = { id: 1, status: 'confirmed', metamaskNetworkId: 'unit test' }
|
||||||
txManager.addTx(tx, onTxDoneCb)
|
txManager.addTx(tx, onTxDoneCb)
|
||||||
var result = txManager.getTxList()
|
var result = txManager.getTxList()
|
||||||
assert.ok(Array.isArray(result))
|
assert.ok(Array.isArray(result))
|
||||||
@ -51,7 +73,7 @@ describe('Transaction Manager', function() {
|
|||||||
it('cuts off early txs beyond a limit', function() {
|
it('cuts off early txs beyond a limit', function() {
|
||||||
const limit = txManager.txHistoryLimit
|
const limit = txManager.txHistoryLimit
|
||||||
for (let i = 0; i < limit + 1; i++) {
|
for (let i = 0; i < limit + 1; i++) {
|
||||||
let tx = { id: i, time: new Date(), status: 'confirmed'}
|
let tx = { id: i, time: new Date(), status: 'confirmed', metamaskNetworkId: 'unit test' }
|
||||||
txManager.addTx(tx, onTxDoneCb)
|
txManager.addTx(tx, onTxDoneCb)
|
||||||
}
|
}
|
||||||
var result = txManager.getTxList()
|
var result = txManager.getTxList()
|
||||||
@ -59,10 +81,10 @@ describe('Transaction Manager', function() {
|
|||||||
assert.equal(result[0].id, 1, 'early txs truncted')
|
assert.equal(result[0].id, 1, 'early txs truncted')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('cuts off early txs beyond a limit weather or not it is confirmed or rejected', function() {
|
it('cuts off early txs beyond a limit whether or not it is confirmed or rejected', function() {
|
||||||
const limit = txManager.txHistoryLimit
|
const limit = txManager.txHistoryLimit
|
||||||
for (let i = 0; i < limit + 1; i++) {
|
for (let i = 0; i < limit + 1; i++) {
|
||||||
let tx = { id: i, time: new Date(), status: 'rejected'}
|
let tx = { id: i, time: new Date(), status: 'rejected', metamaskNetworkId: 'unit test' }
|
||||||
txManager.addTx(tx, onTxDoneCb)
|
txManager.addTx(tx, onTxDoneCb)
|
||||||
}
|
}
|
||||||
var result = txManager.getTxList()
|
var result = txManager.getTxList()
|
||||||
@ -71,11 +93,11 @@ describe('Transaction Manager', function() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('cuts off early txs beyond a limit but does not cut unapproved txs', function() {
|
it('cuts off early txs beyond a limit but does not cut unapproved txs', function() {
|
||||||
var unconfirmedTx = { id: 0, time: new Date(), status: 'unapproved'}
|
var unconfirmedTx = { id: 0, time: new Date(), status: 'unapproved', metamaskNetworkId: 'unit test' }
|
||||||
txManager.addTx(unconfirmedTx, onTxDoneCb)
|
txManager.addTx(unconfirmedTx, onTxDoneCb)
|
||||||
const limit = txManager.txHistoryLimit
|
const limit = txManager.txHistoryLimit
|
||||||
for (let i = 1; i < limit + 1; i++) {
|
for (let i = 1; i < limit + 1; i++) {
|
||||||
let tx = { id: i, time: new Date(), status: 'confirmed'}
|
let tx = { id: i, time: new Date(), status: 'confirmed', metamaskNetworkId: 'unit test' }
|
||||||
txManager.addTx(tx, onTxDoneCb)
|
txManager.addTx(tx, onTxDoneCb)
|
||||||
}
|
}
|
||||||
var result = txManager.getTxList()
|
var result = txManager.getTxList()
|
||||||
@ -88,7 +110,7 @@ describe('Transaction Manager', function() {
|
|||||||
|
|
||||||
describe('#setTxStatusSigned', function() {
|
describe('#setTxStatusSigned', function() {
|
||||||
it('sets the tx status to signed', function() {
|
it('sets the tx status to signed', function() {
|
||||||
var tx = { id: 1, status: 'unapproved' }
|
var tx = { id: 1, status: 'unapproved', metamaskNetworkId: 'unit test' }
|
||||||
txManager.addTx(tx, onTxDoneCb)
|
txManager.addTx(tx, onTxDoneCb)
|
||||||
txManager.setTxStatusSigned(1)
|
txManager.setTxStatusSigned(1)
|
||||||
var result = txManager.getTxList()
|
var result = txManager.getTxList()
|
||||||
@ -99,20 +121,21 @@ describe('Transaction Manager', function() {
|
|||||||
|
|
||||||
it('should emit a signed event to signal the exciton of callback', (done) => {
|
it('should emit a signed event to signal the exciton of callback', (done) => {
|
||||||
this.timeout(10000)
|
this.timeout(10000)
|
||||||
var tx = { id: 1, status: 'unapproved' }
|
var tx = { id: 1, status: 'unapproved', metamaskNetworkId: 'unit test' }
|
||||||
let onTxDoneCb = function (err, txId) {
|
let onTxDoneCb = function () {
|
||||||
assert(true, 'event listener has been triggered and onTxDoneCb executed')
|
assert(true, 'event listener has been triggered and onTxDoneCb executed')
|
||||||
done()
|
done()
|
||||||
}
|
}
|
||||||
txManager.addTx(tx, onTxDoneCb)
|
txManager.addTx(tx)
|
||||||
|
txManager.on('1:signed', onTxDoneCb)
|
||||||
txManager.setTxStatusSigned(1)
|
txManager.setTxStatusSigned(1)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('#setTxStatusRejected', function() {
|
describe('#setTxStatusRejected', function() {
|
||||||
it('sets the tx status to rejected', function() {
|
it('sets the tx status to rejected', function() {
|
||||||
var tx = { id: 1, status: 'unapproved' }
|
var tx = { id: 1, status: 'unapproved', metamaskNetworkId: 'unit test' }
|
||||||
txManager.addTx(tx, onTxDoneCb)
|
txManager.addTx(tx)
|
||||||
txManager.setTxStatusRejected(1)
|
txManager.setTxStatusRejected(1)
|
||||||
var result = txManager.getTxList()
|
var result = txManager.getTxList()
|
||||||
assert.ok(Array.isArray(result))
|
assert.ok(Array.isArray(result))
|
||||||
@ -122,12 +145,13 @@ describe('Transaction Manager', function() {
|
|||||||
|
|
||||||
it('should emit a rejected event to signal the exciton of callback', (done) => {
|
it('should emit a rejected event to signal the exciton of callback', (done) => {
|
||||||
this.timeout(10000)
|
this.timeout(10000)
|
||||||
var tx = { id: 1, status: 'unapproved' }
|
var tx = { id: 1, status: 'unapproved', metamaskNetworkId: 'unit test' }
|
||||||
|
txManager.addTx(tx)
|
||||||
let onTxDoneCb = function (err, txId) {
|
let onTxDoneCb = function (err, txId) {
|
||||||
assert(true, 'event listener has been triggered and onTxDoneCb executed')
|
assert(true, 'event listener has been triggered and onTxDoneCb executed')
|
||||||
done()
|
done()
|
||||||
}
|
}
|
||||||
txManager.addTx(tx, onTxDoneCb)
|
txManager.on('1:rejected', onTxDoneCb)
|
||||||
txManager.setTxStatusRejected(1)
|
txManager.setTxStatusRejected(1)
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -135,9 +159,9 @@ describe('Transaction Manager', function() {
|
|||||||
|
|
||||||
describe('#updateTx', function() {
|
describe('#updateTx', function() {
|
||||||
it('replaces the tx with the same id', function() {
|
it('replaces the tx with the same id', function() {
|
||||||
txManager.addTx({ id: '1', status: 'unapproved' }, onTxDoneCb)
|
txManager.addTx({ id: '1', status: 'unapproved', metamaskNetworkId: 'unit test' }, onTxDoneCb)
|
||||||
txManager.addTx({ id: '2', status: 'confirmed' }, onTxDoneCb)
|
txManager.addTx({ id: '2', status: 'confirmed', metamaskNetworkId: 'unit test' }, onTxDoneCb)
|
||||||
txManager.updateTx({ id: '1', status: 'blah', hash: 'foo' })
|
txManager.updateTx({ id: '1', status: 'blah', hash: 'foo', metamaskNetworkId: 'unit test' })
|
||||||
var result = txManager.getTx('1')
|
var result = txManager.getTx('1')
|
||||||
assert.equal(result.hash, 'foo')
|
assert.equal(result.hash, 'foo')
|
||||||
})
|
})
|
||||||
@ -145,8 +169,8 @@ describe('Transaction Manager', function() {
|
|||||||
|
|
||||||
describe('#getUnapprovedTxList', function() {
|
describe('#getUnapprovedTxList', function() {
|
||||||
it('returns unapproved txs in a hash', function() {
|
it('returns unapproved txs in a hash', function() {
|
||||||
txManager.addTx({ id: '1', status: 'unapproved' }, onTxDoneCb)
|
txManager.addTx({ id: '1', status: 'unapproved', metamaskNetworkId: 'unit test' }, onTxDoneCb)
|
||||||
txManager.addTx({ id: '2', status: 'confirmed' }, onTxDoneCb)
|
txManager.addTx({ id: '2', status: 'confirmed', metamaskNetworkId: 'unit test' }, onTxDoneCb)
|
||||||
let result = txManager.getUnapprovedTxList()
|
let result = txManager.getUnapprovedTxList()
|
||||||
assert.equal(typeof result, 'object')
|
assert.equal(typeof result, 'object')
|
||||||
assert.equal(result['1'].status, 'unapproved')
|
assert.equal(result['1'].status, 'unapproved')
|
||||||
@ -156,8 +180,8 @@ describe('Transaction Manager', function() {
|
|||||||
|
|
||||||
describe('#getTx', function() {
|
describe('#getTx', function() {
|
||||||
it('returns a tx with the requested id', function() {
|
it('returns a tx with the requested id', function() {
|
||||||
txManager.addTx({ id: '1', status: 'unapproved' }, onTxDoneCb)
|
txManager.addTx({ id: '1', status: 'unapproved', metamaskNetworkId: 'unit test' }, onTxDoneCb)
|
||||||
txManager.addTx({ id: '2', status: 'confirmed' }, onTxDoneCb)
|
txManager.addTx({ id: '2', status: 'confirmed', metamaskNetworkId: 'unit test' }, onTxDoneCb)
|
||||||
assert.equal(txManager.getTx('1').status, 'unapproved')
|
assert.equal(txManager.getTx('1').status, 'unapproved')
|
||||||
assert.equal(txManager.getTx('2').status, 'confirmed')
|
assert.equal(txManager.getTx('2').status, 'confirmed')
|
||||||
})
|
})
|
||||||
@ -171,6 +195,7 @@ describe('Transaction Manager', function() {
|
|||||||
let everyOther = i % 2
|
let everyOther = i % 2
|
||||||
txManager.addTx({ id: i,
|
txManager.addTx({ id: i,
|
||||||
status: everyOther ? 'unapproved' : 'confirmed',
|
status: everyOther ? 'unapproved' : 'confirmed',
|
||||||
|
metamaskNetworkId: 'unit test',
|
||||||
txParams: {
|
txParams: {
|
||||||
from: everyOther ? 'foop' : 'zoop',
|
from: everyOther ? 'foop' : 'zoop',
|
||||||
to: everyOther ? 'zoop' : 'foop',
|
to: everyOther ? 'zoop' : 'foop',
|
||||||
|
@ -26,11 +26,10 @@ function mapStateToProps (state) {
|
|||||||
accounts: state.metamask.accounts,
|
accounts: state.metamask.accounts,
|
||||||
address: state.metamask.selectedAccount,
|
address: state.metamask.selectedAccount,
|
||||||
accountDetail: state.appState.accountDetail,
|
accountDetail: state.appState.accountDetail,
|
||||||
transactions: state.metamask.transactions,
|
|
||||||
network: state.metamask.network,
|
network: state.metamask.network,
|
||||||
unconfTxs: valuesFor(state.metamask.unconfTxs),
|
|
||||||
unconfMsgs: valuesFor(state.metamask.unconfMsgs),
|
unconfMsgs: valuesFor(state.metamask.unconfMsgs),
|
||||||
shapeShiftTxList: state.metamask.shapeShiftTxList,
|
shapeShiftTxList: state.metamask.shapeShiftTxList,
|
||||||
|
transactions: state.metamask.selectedAccountTxList || [],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -248,20 +247,10 @@ AccountDetailScreen.prototype.subview = function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
AccountDetailScreen.prototype.transactionList = function () {
|
AccountDetailScreen.prototype.transactionList = function () {
|
||||||
const { transactions, unconfTxs, unconfMsgs, address, network, shapeShiftTxList } = this.props
|
const {transactions, unconfMsgs, address, network, shapeShiftTxList } = this.props
|
||||||
|
|
||||||
var txsToRender = transactions.concat(unconfTxs)
|
|
||||||
// only transactions that are from the current address
|
|
||||||
.filter(tx => tx.txParams.from === address)
|
|
||||||
// only transactions that are on the current network
|
|
||||||
.filter(tx => tx.txParams.metamaskNetworkId === network)
|
|
||||||
// sort by recency
|
|
||||||
.sort((a, b) => b.time - a.time)
|
|
||||||
|
|
||||||
return h(TransactionList, {
|
return h(TransactionList, {
|
||||||
txsToRender,
|
transactions: transactions.sort((a, b) => b.time - a.time),
|
||||||
network,
|
network,
|
||||||
unconfTxs,
|
|
||||||
unconfMsgs,
|
unconfMsgs,
|
||||||
address,
|
address,
|
||||||
shapeShiftTxList,
|
shapeShiftTxList,
|
||||||
|
91
ui/app/accounts/import/index.js
Normal file
91
ui/app/accounts/import/index.js
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
const inherits = require('util').inherits
|
||||||
|
const Component = require('react').Component
|
||||||
|
const h = require('react-hyperscript')
|
||||||
|
const connect = require('react-redux').connect
|
||||||
|
import Select from 'react-select'
|
||||||
|
|
||||||
|
// Subviews
|
||||||
|
const JsonImportView = require('./json.js')
|
||||||
|
const PrivateKeyImportView = require('./private-key.js')
|
||||||
|
|
||||||
|
const menuItems = [
|
||||||
|
'Private Key',
|
||||||
|
'JSON File',
|
||||||
|
]
|
||||||
|
|
||||||
|
module.exports = connect(mapStateToProps)(AccountImportSubview)
|
||||||
|
|
||||||
|
function mapStateToProps (state) {
|
||||||
|
return {
|
||||||
|
menuItems,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inherits(AccountImportSubview, Component)
|
||||||
|
function AccountImportSubview () {
|
||||||
|
Component.call(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
AccountImportSubview.prototype.render = function () {
|
||||||
|
const props = this.props
|
||||||
|
const state = this.state || {}
|
||||||
|
const { menuItems } = props
|
||||||
|
const { type } = state
|
||||||
|
|
||||||
|
return (
|
||||||
|
h('div', {
|
||||||
|
style: {
|
||||||
|
},
|
||||||
|
}, [
|
||||||
|
h('div', {
|
||||||
|
style: {
|
||||||
|
padding: '10px',
|
||||||
|
color: 'rgb(174, 174, 174)',
|
||||||
|
},
|
||||||
|
}, [
|
||||||
|
|
||||||
|
h('h3', { style: { padding: '3px' } }, 'SELECT TYPE'),
|
||||||
|
|
||||||
|
h('style', `
|
||||||
|
.has-value.Select--single > .Select-control .Select-value .Select-value-label, .Select-value-label {
|
||||||
|
color: rgb(174,174,174);
|
||||||
|
}
|
||||||
|
`),
|
||||||
|
|
||||||
|
h(Select, {
|
||||||
|
name: 'import-type-select',
|
||||||
|
clearable: false,
|
||||||
|
value: type || menuItems[0],
|
||||||
|
options: menuItems.map((type) => {
|
||||||
|
return {
|
||||||
|
value: type,
|
||||||
|
label: type,
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
onChange: (opt) => {
|
||||||
|
this.setState({ type: opt.value })
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
|
||||||
|
this.renderImportView(),
|
||||||
|
])
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
AccountImportSubview.prototype.renderImportView = function() {
|
||||||
|
const props = this.props
|
||||||
|
const state = this.state || {}
|
||||||
|
const { type } = state
|
||||||
|
const { menuItems } = props
|
||||||
|
const current = type || menuItems[0]
|
||||||
|
|
||||||
|
switch (current) {
|
||||||
|
case 'Private Key':
|
||||||
|
return h(PrivateKeyImportView)
|
||||||
|
case 'JSON File':
|
||||||
|
return h(JsonImportView)
|
||||||
|
default:
|
||||||
|
return h(JsonImportView)
|
||||||
|
}
|
||||||
|
}
|
98
ui/app/accounts/import/json.js
Normal file
98
ui/app/accounts/import/json.js
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
const inherits = require('util').inherits
|
||||||
|
const Component = require('react').Component
|
||||||
|
const h = require('react-hyperscript')
|
||||||
|
const connect = require('react-redux').connect
|
||||||
|
const actions = require('../../actions')
|
||||||
|
const FileInput = require('react-simple-file-input').default
|
||||||
|
|
||||||
|
module.exports = connect(mapStateToProps)(JsonImportSubview)
|
||||||
|
|
||||||
|
function mapStateToProps (state) {
|
||||||
|
return {
|
||||||
|
error: state.appState.warning,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inherits(JsonImportSubview, Component)
|
||||||
|
function JsonImportSubview () {
|
||||||
|
Component.call(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonImportSubview.prototype.render = function () {
|
||||||
|
const { error } = this.props
|
||||||
|
|
||||||
|
return (
|
||||||
|
h('div', {
|
||||||
|
style: {
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
alignItems: 'center',
|
||||||
|
padding: '5px 15px 0px 15px',
|
||||||
|
},
|
||||||
|
}, [
|
||||||
|
|
||||||
|
h('p', 'Used by a variety of different clients'),
|
||||||
|
|
||||||
|
h(FileInput, {
|
||||||
|
readAs: 'text',
|
||||||
|
onLoad: this.onLoad.bind(this),
|
||||||
|
style: {
|
||||||
|
margin: '20px 0px 12px 20px',
|
||||||
|
fontSize: '15px',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
|
||||||
|
h('input.large-input.letter-spacey', {
|
||||||
|
type: 'password',
|
||||||
|
placeholder: 'Enter password',
|
||||||
|
id: 'json-password-box',
|
||||||
|
onKeyPress: this.createKeyringOnEnter.bind(this),
|
||||||
|
style: {
|
||||||
|
width: 260,
|
||||||
|
marginTop: 12,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
|
||||||
|
h('button.primary', {
|
||||||
|
onClick: this.createNewKeychain.bind(this),
|
||||||
|
style: {
|
||||||
|
margin: 12,
|
||||||
|
},
|
||||||
|
}, 'Import'),
|
||||||
|
|
||||||
|
error ? h('span.warning', error) : null,
|
||||||
|
])
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonImportSubview.prototype.onLoad = function (event, file) {
|
||||||
|
this.setState({file: file, fileContents: event.target.result})
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonImportSubview.prototype.createKeyringOnEnter = function (event) {
|
||||||
|
if (event.key === 'Enter') {
|
||||||
|
event.preventDefault()
|
||||||
|
this.createNewKeychain()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonImportSubview.prototype.createNewKeychain = function () {
|
||||||
|
const state = this.state
|
||||||
|
const { fileContents } = state
|
||||||
|
|
||||||
|
if (!fileContents) {
|
||||||
|
const message = 'You must select a file to import.'
|
||||||
|
return this.props.dispatch(actions.displayWarning(message))
|
||||||
|
}
|
||||||
|
|
||||||
|
const passwordInput = document.getElementById('json-password-box')
|
||||||
|
const password = passwordInput.value
|
||||||
|
|
||||||
|
if (!password) {
|
||||||
|
const message = 'You must enter a password for the selected file.'
|
||||||
|
return this.props.dispatch(actions.displayWarning(message))
|
||||||
|
}
|
||||||
|
|
||||||
|
this.props.dispatch(actions.importNewAccount('JSON File', [ fileContents, password ]))
|
||||||
|
}
|
||||||
|
|
68
ui/app/accounts/import/private-key.js
Normal file
68
ui/app/accounts/import/private-key.js
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
const inherits = require('util').inherits
|
||||||
|
const Component = require('react').Component
|
||||||
|
const h = require('react-hyperscript')
|
||||||
|
const connect = require('react-redux').connect
|
||||||
|
const actions = require('../../actions')
|
||||||
|
|
||||||
|
module.exports = connect(mapStateToProps)(PrivateKeyImportView)
|
||||||
|
|
||||||
|
function mapStateToProps (state) {
|
||||||
|
return {
|
||||||
|
error: state.appState.warning,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inherits(PrivateKeyImportView, Component)
|
||||||
|
function PrivateKeyImportView () {
|
||||||
|
Component.call(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
PrivateKeyImportView.prototype.render = function () {
|
||||||
|
const { error } = this.props
|
||||||
|
|
||||||
|
return (
|
||||||
|
h('div', {
|
||||||
|
style: {
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
alignItems: 'center',
|
||||||
|
padding: '5px 15px 0px 15px',
|
||||||
|
},
|
||||||
|
}, [
|
||||||
|
h('span', 'Paste your private key string here'),
|
||||||
|
|
||||||
|
h('input.large-input.letter-spacey', {
|
||||||
|
type: 'password',
|
||||||
|
id: 'private-key-box',
|
||||||
|
onKeyPress: this.createKeyringOnEnter.bind(this),
|
||||||
|
style: {
|
||||||
|
width: 260,
|
||||||
|
marginTop: 12,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
|
||||||
|
h('button.primary', {
|
||||||
|
onClick: this.createNewKeychain.bind(this),
|
||||||
|
style: {
|
||||||
|
margin: 12,
|
||||||
|
},
|
||||||
|
}, 'Import'),
|
||||||
|
|
||||||
|
error ? h('span.warning', error) : null,
|
||||||
|
])
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
PrivateKeyImportView.prototype.createKeyringOnEnter = function (event) {
|
||||||
|
if (event.key === 'Enter') {
|
||||||
|
event.preventDefault()
|
||||||
|
this.createNewKeychain()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PrivateKeyImportView.prototype.createNewKeychain = function () {
|
||||||
|
const input = document.getElementById('private-key-box')
|
||||||
|
const privateKey = input.value
|
||||||
|
this.props.dispatch(actions.importNewAccount('Private Key', [ privateKey ]))
|
||||||
|
}
|
||||||
|
|
30
ui/app/accounts/import/seed.js
Normal file
30
ui/app/accounts/import/seed.js
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
const inherits = require('util').inherits
|
||||||
|
const Component = require('react').Component
|
||||||
|
const h = require('react-hyperscript')
|
||||||
|
const connect = require('react-redux').connect
|
||||||
|
|
||||||
|
module.exports = connect(mapStateToProps)(SeedImportSubview)
|
||||||
|
|
||||||
|
function mapStateToProps (state) {
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
|
||||||
|
inherits(SeedImportSubview, Component)
|
||||||
|
function SeedImportSubview () {
|
||||||
|
Component.call(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
SeedImportSubview.prototype.render = function () {
|
||||||
|
return (
|
||||||
|
h('div', {
|
||||||
|
style: {
|
||||||
|
},
|
||||||
|
}, [
|
||||||
|
`Paste your seed phrase here!`,
|
||||||
|
h('textarea'),
|
||||||
|
h('br'),
|
||||||
|
h('button', 'Submit'),
|
||||||
|
])
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -73,7 +73,8 @@ AccountsScreen.prototype.render = function () {
|
|||||||
|
|
||||||
const simpleAddress = identity.address.substring(2).toLowerCase()
|
const simpleAddress = identity.address.substring(2).toLowerCase()
|
||||||
const keyring = keyrings.find((kr) => {
|
const keyring = keyrings.find((kr) => {
|
||||||
return kr.accounts.includes(simpleAddress)
|
return kr.accounts.includes(simpleAddress) ||
|
||||||
|
kr.accounts.includes(identity.address)
|
||||||
})
|
})
|
||||||
|
|
||||||
return h(AccountListItem, {
|
return h(AccountListItem, {
|
||||||
@ -154,6 +155,13 @@ AccountsScreen.prototype.addNewAccount = function () {
|
|||||||
this.props.dispatch(actions.addNewAccount(0))
|
this.props.dispatch(actions.addNewAccount(0))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* An optional view proposed in this design:
|
||||||
|
* https://consensys.quip.com/zZVrAysM5znY
|
||||||
|
AccountsScreen.prototype.addNewAccount = function () {
|
||||||
|
this.props.dispatch(actions.navigateToNewAccountScreen())
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
AccountsScreen.prototype.goHome = function () {
|
AccountsScreen.prototype.goHome = function () {
|
||||||
this.props.dispatch(actions.goHome())
|
this.props.dispatch(actions.goHome())
|
||||||
}
|
}
|
||||||
|
@ -32,16 +32,21 @@ var actions = {
|
|||||||
SHOW_INIT_MENU: 'SHOW_INIT_MENU',
|
SHOW_INIT_MENU: 'SHOW_INIT_MENU',
|
||||||
SHOW_NEW_VAULT_SEED: 'SHOW_NEW_VAULT_SEED',
|
SHOW_NEW_VAULT_SEED: 'SHOW_NEW_VAULT_SEED',
|
||||||
SHOW_INFO_PAGE: 'SHOW_INFO_PAGE',
|
SHOW_INFO_PAGE: 'SHOW_INFO_PAGE',
|
||||||
|
SHOW_IMPORT_PAGE: 'SHOW_IMPORT_PAGE',
|
||||||
unlockMetamask: unlockMetamask,
|
unlockMetamask: unlockMetamask,
|
||||||
unlockFailed: unlockFailed,
|
unlockFailed: unlockFailed,
|
||||||
showCreateVault: showCreateVault,
|
showCreateVault: showCreateVault,
|
||||||
showRestoreVault: showRestoreVault,
|
showRestoreVault: showRestoreVault,
|
||||||
showInitializeMenu: showInitializeMenu,
|
showInitializeMenu: showInitializeMenu,
|
||||||
|
showImportPage,
|
||||||
createNewVaultAndKeychain: createNewVaultAndKeychain,
|
createNewVaultAndKeychain: createNewVaultAndKeychain,
|
||||||
createNewVaultAndRestore: createNewVaultAndRestore,
|
createNewVaultAndRestore: createNewVaultAndRestore,
|
||||||
createNewVaultInProgress: createNewVaultInProgress,
|
createNewVaultInProgress: createNewVaultInProgress,
|
||||||
addNewKeyring,
|
addNewKeyring,
|
||||||
|
importNewAccount,
|
||||||
addNewAccount,
|
addNewAccount,
|
||||||
|
NEW_ACCOUNT_SCREEN: 'NEW_ACCOUNT_SCREEN',
|
||||||
|
navigateToNewAccountScreen,
|
||||||
showNewVaultSeed: showNewVaultSeed,
|
showNewVaultSeed: showNewVaultSeed,
|
||||||
showInfoPage: showInfoPage,
|
showInfoPage: showInfoPage,
|
||||||
// seed recovery actions
|
// seed recovery actions
|
||||||
@ -249,7 +254,36 @@ function requestRevealSeed (password) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function addNewKeyring (type, opts) {
|
function addNewKeyring (type, opts) {
|
||||||
return callBackgroundThenUpdate(background.addNewKeyring, type, opts)
|
return (dispatch) => {
|
||||||
|
dispatch(actions.showLoadingIndication())
|
||||||
|
background.addNewKeyring(type, opts, (err, newState) => {
|
||||||
|
dispatch(actions.hideLoadingIndication())
|
||||||
|
if (err) return dispatch(actions.displayWarning(err.message))
|
||||||
|
dispatch(actions.updateMetamaskState(newState))
|
||||||
|
dispatch(actions.showAccountsPage())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function importNewAccount (strategy, args) {
|
||||||
|
return (dispatch) => {
|
||||||
|
dispatch(actions.showLoadingIndication('This may take a while, be patient.'))
|
||||||
|
background.importAccountWithStrategy(strategy, args, (err, newState) => {
|
||||||
|
dispatch(actions.hideLoadingIndication())
|
||||||
|
if (err) return dispatch(actions.displayWarning(err.message))
|
||||||
|
dispatch(actions.updateMetamaskState(newState))
|
||||||
|
dispatch({
|
||||||
|
type: actions.SHOW_ACCOUNT_DETAIL,
|
||||||
|
value: newState.selectedAccount,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function navigateToNewAccountScreen() {
|
||||||
|
return {
|
||||||
|
type: this.NEW_ACCOUNT_SCREEN,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function addNewAccount (ringNumber = 0) {
|
function addNewAccount (ringNumber = 0) {
|
||||||
@ -376,6 +410,12 @@ function showInitializeMenu () {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function showImportPage () {
|
||||||
|
return {
|
||||||
|
type: actions.SHOW_IMPORT_PAGE,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function agreeToDisclaimer () {
|
function agreeToDisclaimer () {
|
||||||
return (dispatch) => {
|
return (dispatch) => {
|
||||||
dispatch(this.showLoadingIndication())
|
dispatch(this.showLoadingIndication())
|
||||||
@ -590,9 +630,10 @@ function useEtherscanProvider () {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function showLoadingIndication () {
|
function showLoadingIndication (message) {
|
||||||
return {
|
return {
|
||||||
type: actions.SHOW_LOADING,
|
type: actions.SHOW_LOADING,
|
||||||
|
value: message,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,6 +20,7 @@ const NoticeScreen = require('./components/notice')
|
|||||||
const generateLostAccountsNotice = require('../lib/lost-accounts-notice')
|
const generateLostAccountsNotice = require('../lib/lost-accounts-notice')
|
||||||
// other views
|
// other views
|
||||||
const ConfigScreen = require('./config')
|
const ConfigScreen = require('./config')
|
||||||
|
const Import = require('./accounts/import')
|
||||||
const InfoScreen = require('./info')
|
const InfoScreen = require('./info')
|
||||||
const LoadingIndicator = require('./components/loading')
|
const LoadingIndicator = require('./components/loading')
|
||||||
const SandwichExpando = require('sandwich-expando')
|
const SandwichExpando = require('sandwich-expando')
|
||||||
@ -42,6 +43,7 @@ function mapStateToProps (state) {
|
|||||||
return {
|
return {
|
||||||
// state from plugin
|
// state from plugin
|
||||||
isLoading: state.appState.isLoading,
|
isLoading: state.appState.isLoading,
|
||||||
|
loadingMessage: state.appState.loadingMessage,
|
||||||
isDisclaimerConfirmed: state.metamask.isDisclaimerConfirmed,
|
isDisclaimerConfirmed: state.metamask.isDisclaimerConfirmed,
|
||||||
noActiveNotices: state.metamask.noActiveNotices,
|
noActiveNotices: state.metamask.noActiveNotices,
|
||||||
isInitialized: state.metamask.isInitialized,
|
isInitialized: state.metamask.isInitialized,
|
||||||
@ -63,7 +65,7 @@ function mapStateToProps (state) {
|
|||||||
|
|
||||||
App.prototype.render = function () {
|
App.prototype.render = function () {
|
||||||
var props = this.props
|
var props = this.props
|
||||||
const { isLoading, transForward } = props
|
const { isLoading, loadingMessage, transForward } = props
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
||||||
@ -75,7 +77,7 @@ App.prototype.render = function () {
|
|||||||
},
|
},
|
||||||
}, [
|
}, [
|
||||||
|
|
||||||
h(LoadingIndicator, { isLoading }),
|
h(LoadingIndicator, { isLoading, loadingMessage }),
|
||||||
|
|
||||||
// app bar
|
// app bar
|
||||||
this.renderAppBar(),
|
this.renderAppBar(),
|
||||||
@ -304,6 +306,13 @@ App.prototype.renderDropdown = function () {
|
|||||||
icon: h('i.fa.fa-gear.fa-lg'),
|
icon: h('i.fa.fa-gear.fa-lg'),
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
h(DropMenuItem, {
|
||||||
|
label: 'Import Account',
|
||||||
|
closeMenu: () => this.setState({ isMainMenuOpen: !isOpen }),
|
||||||
|
action: () => this.props.dispatch(actions.showImportPage()),
|
||||||
|
icon: h('i.fa.fa-arrow-circle-o-up.fa-lg'),
|
||||||
|
}),
|
||||||
|
|
||||||
h(DropMenuItem, {
|
h(DropMenuItem, {
|
||||||
label: 'Lock',
|
label: 'Lock',
|
||||||
closeMenu: () => this.setState({ isMainMenuOpen: !isOpen }),
|
closeMenu: () => this.setState({ isMainMenuOpen: !isOpen }),
|
||||||
@ -411,6 +420,9 @@ App.prototype.renderPrimary = function () {
|
|||||||
case 'config':
|
case 'config':
|
||||||
return h(ConfigScreen, {key: 'config'})
|
return h(ConfigScreen, {key: 'config'})
|
||||||
|
|
||||||
|
case 'import-menu':
|
||||||
|
return h(Import, {key: 'import-menu'})
|
||||||
|
|
||||||
case 'reveal-seed-conf':
|
case 'reveal-seed-conf':
|
||||||
return h(RevealSeedConfirmation, {key: 'reveal-seed-conf'})
|
return h(RevealSeedConfirmation, {key: 'reveal-seed-conf'})
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ const CoinbaseForm = require('./coinbase-form')
|
|||||||
const ShapeshiftForm = require('./shapeshift-form')
|
const ShapeshiftForm = require('./shapeshift-form')
|
||||||
const extension = require('../../../app/scripts/lib/extension')
|
const extension = require('../../../app/scripts/lib/extension')
|
||||||
const Loading = require('./loading')
|
const Loading = require('./loading')
|
||||||
|
const TabBar = require('./tab-bar')
|
||||||
|
|
||||||
module.exports = connect(mapStateToProps)(BuyButtonSubview)
|
module.exports = connect(mapStateToProps)(BuyButtonSubview)
|
||||||
|
|
||||||
@ -29,7 +30,6 @@ function BuyButtonSubview () {
|
|||||||
|
|
||||||
BuyButtonSubview.prototype.render = function () {
|
BuyButtonSubview.prototype.render = function () {
|
||||||
const props = this.props
|
const props = this.props
|
||||||
const currentForm = props.buyView.formView
|
|
||||||
const isLoading = props.isSubLoading
|
const isLoading = props.isSubLoading
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -53,43 +53,53 @@ BuyButtonSubview.prototype.render = function () {
|
|||||||
|
|
||||||
h(Loading, { isLoading }),
|
h(Loading, { isLoading }),
|
||||||
|
|
||||||
h('h3.flex-row.text-transform-uppercase', {
|
h(TabBar, {
|
||||||
style: {
|
tabs: [
|
||||||
background: '#EBEBEB',
|
{
|
||||||
color: '#AEAEAE',
|
content: [
|
||||||
paddingTop: '4px',
|
'Coinbase',
|
||||||
justifyContent: 'space-around',
|
h('a', {
|
||||||
|
onClick: (event) => this.navigateTo('https://github.com/MetaMask/faq/blob/master/COINBASE.md'),
|
||||||
|
}, [
|
||||||
|
h('i.fa.fa-question-circle', {
|
||||||
|
style: {
|
||||||
|
margin: '0px 5px',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
],
|
||||||
|
key: 'coinbase',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
content: [
|
||||||
|
'Shapeshift',
|
||||||
|
h('a', {
|
||||||
|
href: 'https://github.com/MetaMask/faq/blob/master/COINBASE.md',
|
||||||
|
onClick: (event) => this.navigateTo('https://info.shapeshift.io/about'),
|
||||||
|
}, [
|
||||||
|
h('i.fa.fa-question-circle', {
|
||||||
|
style: {
|
||||||
|
margin: '0px 5px',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
],
|
||||||
|
key: 'shapeshift',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
defaultTab: 'coinbase',
|
||||||
|
tabSelected: (key) => {
|
||||||
|
switch (key) {
|
||||||
|
case 'coinbase':
|
||||||
|
props.dispatch(actions.coinBaseSubview())
|
||||||
|
break
|
||||||
|
case 'shapeshift':
|
||||||
|
props.dispatch(actions.shapeShiftSubview(props.provider.type))
|
||||||
|
break
|
||||||
|
}
|
||||||
},
|
},
|
||||||
}, [
|
}),
|
||||||
h(currentForm.coinbase ? '.activeForm' : '.inactiveForm.pointer', {
|
|
||||||
onClick: () => props.dispatch(actions.coinBaseSubview()),
|
|
||||||
}, 'Coinbase'),
|
|
||||||
h('a', {
|
|
||||||
onClick: (event) => this.navigateTo('https://github.com/MetaMask/faq/blob/master/COINBASE.md'),
|
|
||||||
}, [
|
|
||||||
h('i.fa.fa-question-circle', {
|
|
||||||
style: {
|
|
||||||
position: 'relative',
|
|
||||||
right: '33px',
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
]),
|
|
||||||
h(currentForm.shapeshift ? '.activeForm' : '.inactiveForm.pointer', {
|
|
||||||
onClick: () => props.dispatch(actions.shapeShiftSubview(props.provider.type)),
|
|
||||||
}, 'Shapeshift'),
|
|
||||||
|
|
||||||
h('a', {
|
|
||||||
href: 'https://github.com/MetaMask/faq/blob/master/COINBASE.md',
|
|
||||||
onClick: (event) => this.navigateTo('https://info.shapeshift.io/about'),
|
|
||||||
}, [
|
|
||||||
h('i.fa.fa-question-circle', {
|
|
||||||
style: {
|
|
||||||
position: 'relative',
|
|
||||||
right: '28px',
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
]),
|
|
||||||
]),
|
|
||||||
this.formVersionSubview(),
|
this.formVersionSubview(),
|
||||||
])
|
])
|
||||||
)
|
)
|
||||||
|
@ -72,7 +72,7 @@ CoinbaseForm.prototype.render = function () {
|
|||||||
lineHeight: '13px',
|
lineHeight: '13px',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
`there is a USD$ 5 a day max and a USD$ 50
|
`there is a USD$ 15 a day max and a USD$ 50
|
||||||
dollar limit per the life time of an account without a
|
dollar limit per the life time of an account without a
|
||||||
coinbase account. A fee of 3.75% will be aplied to debit/credit cards.`),
|
coinbase account. A fee of 3.75% will be aplied to debit/credit cards.`),
|
||||||
|
|
||||||
@ -136,14 +136,14 @@ CoinbaseForm.prototype.renderLoading = function () {
|
|||||||
function isValidAmountforCoinBase (amount) {
|
function isValidAmountforCoinBase (amount) {
|
||||||
amount = parseFloat(amount)
|
amount = parseFloat(amount)
|
||||||
if (amount) {
|
if (amount) {
|
||||||
if (amount <= 5 && amount > 0) {
|
if (amount <= 15 && amount > 0) {
|
||||||
return {
|
return {
|
||||||
valid: true,
|
valid: true,
|
||||||
}
|
}
|
||||||
} else if (amount > 5) {
|
} else if (amount > 15) {
|
||||||
return {
|
return {
|
||||||
valid: false,
|
valid: false,
|
||||||
message: 'The amount can not be greater then $5',
|
message: 'The amount can not be greater then $15',
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return {
|
return {
|
||||||
|
@ -12,7 +12,7 @@ function LoadingIndicator () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
LoadingIndicator.prototype.render = function () {
|
LoadingIndicator.prototype.render = function () {
|
||||||
var isLoading = this.props.isLoading
|
const { isLoading, loadingMessage } = this.props
|
||||||
|
|
||||||
return (
|
return (
|
||||||
h(ReactCSSTransitionGroup, {
|
h(ReactCSSTransitionGroup, {
|
||||||
@ -37,8 +37,14 @@ LoadingIndicator.prototype.render = function () {
|
|||||||
h('img', {
|
h('img', {
|
||||||
src: 'images/loading.svg',
|
src: 'images/loading.svg',
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
showMessageIfAny(loadingMessage),
|
||||||
]) : null,
|
]) : null,
|
||||||
])
|
])
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function showMessageIfAny (loadingMessage) {
|
||||||
|
if (!loadingMessage) return null
|
||||||
|
return h('span', loadingMessage)
|
||||||
|
}
|
||||||
|
@ -7,8 +7,6 @@ const EthBalance = require('./eth-balance')
|
|||||||
const util = require('../util')
|
const util = require('../util')
|
||||||
const addressSummary = util.addressSummary
|
const addressSummary = util.addressSummary
|
||||||
const nameForAddress = require('../../lib/contract-namer')
|
const nameForAddress = require('../../lib/contract-namer')
|
||||||
const ethUtil = require('ethereumjs-util')
|
|
||||||
const BN = ethUtil.BN
|
|
||||||
|
|
||||||
module.exports = PendingTxDetails
|
module.exports = PendingTxDetails
|
||||||
|
|
||||||
@ -29,15 +27,9 @@ PTXP.render = function () {
|
|||||||
var account = props.accounts[address]
|
var account = props.accounts[address]
|
||||||
var balance = account ? account.balance : '0x0'
|
var balance = account ? account.balance : '0x0'
|
||||||
|
|
||||||
var gasMultiplier = txData.gasMultiplier
|
var txFee = txData.txFee || ''
|
||||||
var gasCost = new BN(ethUtil.stripHexPrefix(txParams.gas || txData.estimatedGas), 16)
|
var maxCost = txData.maxCost || ''
|
||||||
var gasPrice = new BN(ethUtil.stripHexPrefix(txParams.gasPrice || '0x4a817c800'), 16)
|
|
||||||
gasPrice = gasPrice.mul(new BN(gasMultiplier * 100), 10).div(new BN(100, 10))
|
|
||||||
var txFee = gasCost.mul(gasPrice)
|
|
||||||
var txValue = new BN(ethUtil.stripHexPrefix(txParams.value || '0x0'), 16)
|
|
||||||
var maxCost = txValue.add(txFee)
|
|
||||||
var dataLength = txParams.data ? (txParams.data.length - 2) / 2 : 0
|
var dataLength = txParams.data ? (txParams.data.length - 2) / 2 : 0
|
||||||
|
|
||||||
var imageify = props.imageifyIdenticons === undefined ? true : props.imageifyIdenticons
|
var imageify = props.imageifyIdenticons === undefined ? true : props.imageifyIdenticons
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
35
ui/app/components/tab-bar.js
Normal file
35
ui/app/components/tab-bar.js
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
const Component = require('react').Component
|
||||||
|
const h = require('react-hyperscript')
|
||||||
|
const inherits = require('util').inherits
|
||||||
|
|
||||||
|
module.exports = TabBar
|
||||||
|
|
||||||
|
inherits(TabBar, Component)
|
||||||
|
function TabBar () {
|
||||||
|
Component.call(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
TabBar.prototype.render = function () {
|
||||||
|
const props = this.props
|
||||||
|
const state = this.state || {}
|
||||||
|
const { tabs = [], defaultTab, tabSelected } = props
|
||||||
|
const { subview = defaultTab } = state
|
||||||
|
|
||||||
|
return (
|
||||||
|
h('.flex-row.space-around.text-transform-uppercase', {
|
||||||
|
style: {
|
||||||
|
background: '#EBEBEB',
|
||||||
|
color: '#AEAEAE',
|
||||||
|
paddingTop: '4px',
|
||||||
|
},
|
||||||
|
}, tabs.map((tab) => {
|
||||||
|
const { key, content } = tab
|
||||||
|
return h(subview === key ? '.activeForm' : '.inactiveForm.pointer', {
|
||||||
|
onClick: () => {
|
||||||
|
this.setState({ subview: key })
|
||||||
|
tabSelected(key)
|
||||||
|
},
|
||||||
|
}, content)
|
||||||
|
}))
|
||||||
|
)
|
||||||
|
}
|
@ -13,13 +13,40 @@ function TransactionIcon () {
|
|||||||
|
|
||||||
TransactionIcon.prototype.render = function () {
|
TransactionIcon.prototype.render = function () {
|
||||||
const { transaction, txParams, isMsg } = this.props
|
const { transaction, txParams, isMsg } = this.props
|
||||||
|
switch (transaction.status) {
|
||||||
|
case 'unapproved':
|
||||||
|
return h('.unapproved-tx', {
|
||||||
|
style: {
|
||||||
|
width: '24px',
|
||||||
|
height: '24px',
|
||||||
|
background: '#4dffff',
|
||||||
|
border: 'solid',
|
||||||
|
borderColor: '#AEAEAE',
|
||||||
|
borderWidth: '0.5px',
|
||||||
|
borderRadius: '13px',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
if (transaction.status === 'rejected') {
|
case 'rejected':
|
||||||
return h('i.fa.fa-exclamation-triangle.fa-lg.warning', {
|
return h('i.fa.fa-exclamation-triangle.fa-lg.warning', {
|
||||||
style: {
|
style: {
|
||||||
width: '24px',
|
width: '24px',
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
case 'failed':
|
||||||
|
return h('i.fa.fa-exclamation-triangle.fa-lg.error', {
|
||||||
|
style: {
|
||||||
|
width: '24px',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
case 'submitted':
|
||||||
|
return h('i.fa.fa-ellipsis-h', {
|
||||||
|
style: {
|
||||||
|
fontSize: '27px',
|
||||||
|
},
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isMsg) {
|
if (isMsg) {
|
||||||
|
@ -8,6 +8,7 @@ const explorerLink = require('../../lib/explorer-link')
|
|||||||
const CopyButton = require('./copyButton')
|
const CopyButton = require('./copyButton')
|
||||||
const vreme = new (require('vreme'))
|
const vreme = new (require('vreme'))
|
||||||
const extension = require('../../../app/scripts/lib/extension')
|
const extension = require('../../../app/scripts/lib/extension')
|
||||||
|
const Tooltip = require('./tooltip')
|
||||||
|
|
||||||
const TransactionIcon = require('./transaction-list-item-icon')
|
const TransactionIcon = require('./transaction-list-item-icon')
|
||||||
const ShiftListItem = require('./shift-list-item')
|
const ShiftListItem = require('./shift-list-item')
|
||||||
@ -27,7 +28,7 @@ TransactionListItem.prototype.render = function () {
|
|||||||
|
|
||||||
let isLinkable = false
|
let isLinkable = false
|
||||||
const numericNet = parseInt(network)
|
const numericNet = parseInt(network)
|
||||||
isLinkable = numericNet === 1 || numericNet === 2
|
isLinkable = numericNet === 1 || numericNet === 3
|
||||||
|
|
||||||
var isMsg = ('msgParams' in transaction)
|
var isMsg = ('msgParams' in transaction)
|
||||||
var isTx = ('txParams' in transaction)
|
var isTx = ('txParams' in transaction)
|
||||||
@ -41,7 +42,6 @@ TransactionListItem.prototype.render = function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const isClickable = ('hash' in transaction && isLinkable) || isPending
|
const isClickable = ('hash' in transaction && isLinkable) || isPending
|
||||||
|
|
||||||
return (
|
return (
|
||||||
h(`.transaction-list-item.flex-row.flex-space-between${isClickable ? '.pointer' : ''}`, {
|
h(`.transaction-list-item.flex-row.flex-space-between${isClickable ? '.pointer' : ''}`, {
|
||||||
onClick: (event) => {
|
onClick: (event) => {
|
||||||
@ -59,11 +59,7 @@ TransactionListItem.prototype.render = function () {
|
|||||||
}, [
|
}, [
|
||||||
|
|
||||||
h('.identicon-wrapper.flex-column.flex-center.select-none', [
|
h('.identicon-wrapper.flex-column.flex-center.select-none', [
|
||||||
transaction.status === 'unapproved' ? h('i.fa.fa-ellipsis-h', {
|
h('.pop-hover', {
|
||||||
style: {
|
|
||||||
fontSize: '27px',
|
|
||||||
},
|
|
||||||
}) : h('.pop-hover', {
|
|
||||||
onClick: (event) => {
|
onClick: (event) => {
|
||||||
event.stopPropagation()
|
event.stopPropagation()
|
||||||
if (!isTx || isPending) return
|
if (!isTx || isPending) return
|
||||||
@ -139,7 +135,14 @@ function failIfFailed (transaction) {
|
|||||||
if (transaction.status === 'rejected') {
|
if (transaction.status === 'rejected') {
|
||||||
return h('span.error', ' (Rejected)')
|
return h('span.error', ' (Rejected)')
|
||||||
}
|
}
|
||||||
if (transaction.status === 'failed') {
|
if (transaction.err) {
|
||||||
return h('span.error', ' (Failed)')
|
|
||||||
|
return h(Tooltip, {
|
||||||
|
title: transaction.err.message,
|
||||||
|
position: 'bottom',
|
||||||
|
}, [
|
||||||
|
h('span.error', ' (Failed)'),
|
||||||
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -13,12 +13,13 @@ function TransactionList () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TransactionList.prototype.render = function () {
|
TransactionList.prototype.render = function () {
|
||||||
const { txsToRender, network, unconfMsgs } = this.props
|
const { transactions, network, unconfMsgs } = this.props
|
||||||
|
|
||||||
var shapeShiftTxList
|
var shapeShiftTxList
|
||||||
if (network === '1') {
|
if (network === '1') {
|
||||||
shapeShiftTxList = this.props.shapeShiftTxList
|
shapeShiftTxList = this.props.shapeShiftTxList
|
||||||
}
|
}
|
||||||
const transactions = !shapeShiftTxList ? txsToRender.concat(unconfMsgs) : txsToRender.concat(unconfMsgs, shapeShiftTxList)
|
const txsToRender = !shapeShiftTxList ? transactions.concat(unconfMsgs) : transactions.concat(unconfMsgs, shapeShiftTxList)
|
||||||
.sort((a, b) => b.time - a.time)
|
.sort((a, b) => b.time - a.time)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -55,8 +56,8 @@ TransactionList.prototype.render = function () {
|
|||||||
},
|
},
|
||||||
}, [
|
}, [
|
||||||
|
|
||||||
transactions.length
|
txsToRender.length
|
||||||
? transactions.map((transaction, i) => {
|
? txsToRender.map((transaction, i) => {
|
||||||
let key
|
let key
|
||||||
switch (transaction.key) {
|
switch (transaction.key) {
|
||||||
case 'shapeshift':
|
case 'shapeshift':
|
||||||
|
@ -41,11 +41,13 @@ ConfirmTxScreen.prototype.render = function () {
|
|||||||
var provider = state.provider
|
var provider = state.provider
|
||||||
var unconfTxs = state.unconfTxs
|
var unconfTxs = state.unconfTxs
|
||||||
var unconfMsgs = state.unconfMsgs
|
var unconfMsgs = state.unconfMsgs
|
||||||
|
|
||||||
var unconfTxList = txHelper(unconfTxs, unconfMsgs, network)
|
var unconfTxList = txHelper(unconfTxs, unconfMsgs, network)
|
||||||
var index = state.index !== undefined ? state.index : 0
|
var index = state.index !== undefined && unconfTxList[index] ? state.index : 0
|
||||||
var txData = unconfTxList[index] || unconfTxList[0] || {}
|
var txData = unconfTxList[index] || {}
|
||||||
var txParams = txData.txParams || {}
|
var txParams = txData.params || {}
|
||||||
var isNotification = isPopupOrNotification() === 'notification'
|
var isNotification = isPopupOrNotification() === 'notification'
|
||||||
|
if (unconfTxList.length === 0) return null
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
||||||
@ -115,27 +117,24 @@ ConfirmTxScreen.prototype.render = function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function currentTxView (opts) {
|
function currentTxView (opts) {
|
||||||
if ('txParams' in opts.txData) {
|
const { txData } = opts
|
||||||
|
const { txParams, msgParams } = txData
|
||||||
|
|
||||||
|
if (txParams) {
|
||||||
// This is a pending transaction
|
// This is a pending transaction
|
||||||
return h(PendingTx, opts)
|
return h(PendingTx, opts)
|
||||||
} else if ('msgParams' in opts.txData) {
|
} else if (msgParams) {
|
||||||
// This is a pending message to sign
|
// This is a pending message to sign
|
||||||
return h(PendingMsg, opts)
|
return h(PendingMsg, opts)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ConfirmTxScreen.prototype.checkBalanceAgainstTx = function (txData) {
|
ConfirmTxScreen.prototype.checkBalanceAgainstTx = function (txData) {
|
||||||
|
if (!txData.txParams) return false
|
||||||
var state = this.props
|
var state = this.props
|
||||||
|
var address = txData.txParams.from || state.selectedAccount
|
||||||
var txParams = txData.txParams || {}
|
|
||||||
var address = txParams.from || state.selectedAccount
|
|
||||||
var account = state.accounts[address]
|
var account = state.accounts[address]
|
||||||
var balance = account ? account.balance : '0x0'
|
var balance = account ? account.balance : '0x0'
|
||||||
|
var maxCost = new BN(txData.maxCost)
|
||||||
var gasCost = new BN(ethUtil.stripHexPrefix(txParams.gas || txData.estimatedGas), 16)
|
|
||||||
var gasPrice = new BN(ethUtil.stripHexPrefix(txParams.gasPrice || '0x4a817c800'), 16)
|
|
||||||
var txFee = gasCost.mul(gasPrice)
|
|
||||||
var txValue = new BN(ethUtil.stripHexPrefix(txParams.value || '0x0'), 16)
|
|
||||||
var maxCost = txValue.add(txFee)
|
|
||||||
|
|
||||||
var balanceBn = new BN(ethUtil.stripHexPrefix(balance), 16)
|
var balanceBn = new BN(ethUtil.stripHexPrefix(balance), 16)
|
||||||
return maxCost.gt(balanceBn)
|
return maxCost.gt(balanceBn)
|
||||||
|
@ -23,6 +23,14 @@
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.space-between {
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.space-around {
|
||||||
|
justify-content: space-around;
|
||||||
|
}
|
||||||
|
|
||||||
.flex-column-bottom {
|
.flex-column-bottom {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column-reverse;
|
flex-direction: column-reverse;
|
||||||
|
@ -110,7 +110,7 @@ InfoScreen.prototype.render = function () {
|
|||||||
onClick (event) { this.navigateTo(event.target.href) },
|
onClick (event) { this.navigateTo(event.target.href) },
|
||||||
}, [
|
}, [
|
||||||
h('img.icon-size', {
|
h('img.icon-size', {
|
||||||
src: manifest.icons[128],
|
src: manifest.icons['128'],
|
||||||
style: {
|
style: {
|
||||||
filter: 'grayscale(100%)', /* IE6-9 */
|
filter: 'grayscale(100%)', /* IE6-9 */
|
||||||
WebkitFilter: 'grayscale(100%)', /* Microsoft Edge and Firefox 35+ */
|
WebkitFilter: 'grayscale(100%)', /* Microsoft Edge and Firefox 35+ */
|
||||||
|
@ -99,6 +99,14 @@ function reduceApp (state, action) {
|
|||||||
transForward: action.value,
|
transForward: action.value,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
case actions.SHOW_IMPORT_PAGE:
|
||||||
|
return extend(appState, {
|
||||||
|
currentView: {
|
||||||
|
name: 'import-menu',
|
||||||
|
},
|
||||||
|
transForward: true,
|
||||||
|
})
|
||||||
|
|
||||||
case actions.SHOW_INFO_PAGE:
|
case actions.SHOW_INFO_PAGE:
|
||||||
return extend(appState, {
|
return extend(appState, {
|
||||||
currentView: {
|
currentView: {
|
||||||
@ -128,6 +136,15 @@ function reduceApp (state, action) {
|
|||||||
isLoading: false,
|
isLoading: false,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
case actions.NEW_ACCOUNT_SCREEN:
|
||||||
|
return extend(appState, {
|
||||||
|
currentView: {
|
||||||
|
name: 'new-account',
|
||||||
|
context: appState.currentView.context,
|
||||||
|
},
|
||||||
|
transForward: true,
|
||||||
|
})
|
||||||
|
|
||||||
case actions.SHOW_SEND_PAGE:
|
case actions.SHOW_SEND_PAGE:
|
||||||
return extend(appState, {
|
return extend(appState, {
|
||||||
currentView: {
|
currentView: {
|
||||||
@ -369,6 +386,7 @@ function reduceApp (state, action) {
|
|||||||
case actions.SHOW_LOADING:
|
case actions.SHOW_LOADING:
|
||||||
return extend(appState, {
|
return extend(appState, {
|
||||||
isLoading: true,
|
isLoading: true,
|
||||||
|
loadingMessage: action.value,
|
||||||
})
|
})
|
||||||
|
|
||||||
case actions.HIDE_LOADING:
|
case actions.HIDE_LOADING:
|
||||||
@ -446,7 +464,7 @@ function reduceApp (state, action) {
|
|||||||
},
|
},
|
||||||
buyView: {
|
buyView: {
|
||||||
subview: 'buyForm',
|
subview: 'buyForm',
|
||||||
amount: '5.00',
|
amount: '15.00',
|
||||||
buyAddress: action.value,
|
buyAddress: action.value,
|
||||||
formView: {
|
formView: {
|
||||||
coinbase: true,
|
coinbase: true,
|
||||||
|
@ -26,7 +26,7 @@ UnlockScreen.prototype.render = function () {
|
|||||||
const state = this.props
|
const state = this.props
|
||||||
const warning = state.warning
|
const warning = state.warning
|
||||||
return (
|
return (
|
||||||
h('.flex-column.hey-im-here', [
|
h('.flex-column', [
|
||||||
h('.unlock-screen.flex-column.flex-center.flex-grow', [
|
h('.unlock-screen.flex-column.flex-center.flex-grow', [
|
||||||
|
|
||||||
h(Mascot, {
|
h(Mascot, {
|
||||||
|
@ -10,6 +10,7 @@ var cssFiles = {
|
|||||||
'index.css': fs.readFileSync(path.join(__dirname, '/app/css/index.css'), 'utf8'),
|
'index.css': fs.readFileSync(path.join(__dirname, '/app/css/index.css'), 'utf8'),
|
||||||
'transitions.css': fs.readFileSync(path.join(__dirname, '/app/css/transitions.css'), 'utf8'),
|
'transitions.css': fs.readFileSync(path.join(__dirname, '/app/css/transitions.css'), 'utf8'),
|
||||||
'react-tooltip-component.css': fs.readFileSync(path.join(__dirname, '..', 'node_modules', 'react-tooltip-component', 'dist', 'react-tooltip-component.css'), 'utf8'),
|
'react-tooltip-component.css': fs.readFileSync(path.join(__dirname, '..', 'node_modules', 'react-tooltip-component', 'dist', 'react-tooltip-component.css'), 'utf8'),
|
||||||
|
'react-css': fs.readFileSync(path.join(__dirname, '..', 'node_modules', 'react-select', 'dist', 'react-select.css'), 'utf8'),
|
||||||
}
|
}
|
||||||
|
|
||||||
function bundleCss () {
|
function bundleCss () {
|
||||||
|
@ -5,7 +5,7 @@ module.exports = function (hash, network) {
|
|||||||
case 1: // main net
|
case 1: // main net
|
||||||
prefix = ''
|
prefix = ''
|
||||||
break
|
break
|
||||||
case 2: // morden test net
|
case 3: // morden test net
|
||||||
prefix = 'testnet.'
|
prefix = 'testnet.'
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
|
Loading…
Reference in New Issue
Block a user