mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 09:52:26 +01:00
Merge branch 'dev' into conversion-api-err
This commit is contained in:
commit
c53932a19a
11
CHANGELOG.md
11
CHANGELOG.md
@ -2,6 +2,15 @@
|
||||
|
||||
## Current Master
|
||||
|
||||
## 3.0.1 2017-1-17
|
||||
|
||||
- Fixed bug that prevented eth.sign from working.
|
||||
|
||||
## 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
|
||||
- Override RPC commands eth_syncing and web3_clientVersion
|
||||
- Remove certain non-essential permissions from certain builds.
|
||||
@ -14,6 +23,8 @@
|
||||
|
||||
## 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.
|
||||
- Implemented basic checking for valid RPC URIs.
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "MetaMask",
|
||||
"short_name": "Metamask",
|
||||
"version": "2.14.1",
|
||||
"version": "3.0.1",
|
||||
"manifest_version": 2,
|
||||
"author": "https://metamask.io",
|
||||
"description": "Ethereum Browser Extension",
|
||||
|
@ -22,12 +22,11 @@ const controller = new MetamaskController({
|
||||
setData,
|
||||
loadData,
|
||||
})
|
||||
const txManager = controller.txManager
|
||||
|
||||
function triggerUi () {
|
||||
if (!popupIsOpen) notification.show()
|
||||
}
|
||||
// On first install, open a window to MetaMask website to how-it-works.
|
||||
|
||||
extension.runtime.onInstalled.addListener(function (details) {
|
||||
if ((details.reason === 'install') && (!METAMASK_DEBUG)) {
|
||||
extension.tabs.create({url: 'https://metamask.io/#how-it-works'})
|
||||
@ -94,7 +93,8 @@ function setupControllerConnection (stream) {
|
||||
// plugin badge text
|
||||
//
|
||||
|
||||
txManager.on('updateBadge', updateBadge)
|
||||
controller.txManager.on('updateBadge', updateBadge)
|
||||
updateBadge()
|
||||
|
||||
function updateBadge () {
|
||||
var label = ''
|
||||
|
@ -95,7 +95,6 @@ module.exports = class KeyringController extends EventEmitter {
|
||||
isInitialized: (!!wallet || !!vault),
|
||||
isUnlocked: Boolean(this.password),
|
||||
isDisclaimerConfirmed: this.configManager.getConfirmedDisclaimer(),
|
||||
transactions: this.configManager.getTxList(),
|
||||
unconfMsgs: messageManager.unconfirmedMsgs(),
|
||||
messages: messageManager.getMsgList(),
|
||||
selectedAccount: address,
|
||||
@ -273,7 +272,7 @@ module.exports = class KeyringController extends EventEmitter {
|
||||
setSelectedAccount (address) {
|
||||
var addr = normalize(address)
|
||||
this.configManager.setSelectedAccount(addr)
|
||||
return Promise.resolve(addr)
|
||||
return this.fullUpdate()
|
||||
}
|
||||
|
||||
// Save Account Label
|
||||
@ -317,13 +316,11 @@ module.exports = class KeyringController extends EventEmitter {
|
||||
// This method signs tx and returns a promise for
|
||||
// TX Manager to update the state after signing
|
||||
|
||||
signTransaction (ethTx, selectedAddress, txId) {
|
||||
const address = normalize(selectedAddress)
|
||||
return this.getKeyringForAccount(address)
|
||||
signTransaction (ethTx, _fromAddress) {
|
||||
const fromAddress = normalize(_fromAddress)
|
||||
return this.getKeyringForAccount(fromAddress)
|
||||
.then((keyring) => {
|
||||
return keyring.signTransaction(address, ethTx)
|
||||
}).then((tx) => {
|
||||
return {tx, txId}
|
||||
return keyring.signTransaction(fromAddress, ethTx)
|
||||
})
|
||||
}
|
||||
// Add Unconfirmed Message
|
||||
@ -400,6 +397,7 @@ module.exports = class KeyringController extends EventEmitter {
|
||||
}).then((rawSig) => {
|
||||
cb(null, rawSig)
|
||||
approvalCb(null, true)
|
||||
messageManager.confirmMsg(msgId)
|
||||
return rawSig
|
||||
})
|
||||
} catch (e) {
|
||||
|
@ -76,7 +76,7 @@ class HdKeyring extends EventEmitter {
|
||||
// For eth_sign, we need to sign transactions:
|
||||
signMessage (withAccount, data) {
|
||||
const wallet = this._getWalletForAccount(withAccount)
|
||||
const message = ethUtil.removeHexPrefix(data)
|
||||
const message = ethUtil.stripHexPrefix(data)
|
||||
var privKey = wallet.getPrivateKey()
|
||||
var msgSig = ethUtil.ecsign(new Buffer(message, 'hex'), privKey)
|
||||
var rawMsgSig = ethUtil.bufferToHex(sigUtil.concatSig(msgSig.v, msgSig.r, msgSig.s))
|
||||
|
@ -35,12 +35,12 @@ class SimpleKeyring extends EventEmitter {
|
||||
newWallets.push(Wallet.generate())
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
||||
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.
|
||||
@ -54,7 +54,7 @@ class SimpleKeyring extends EventEmitter {
|
||||
// For eth_sign, we need to sign transactions:
|
||||
signMessage (withAccount, data) {
|
||||
const wallet = this._getWalletForAccount(withAccount)
|
||||
const message = ethUtil.removeHexPrefix(data)
|
||||
const message = ethUtil.stripHexPrefix(data)
|
||||
var privKey = wallet.getPrivateKey()
|
||||
var msgSig = ethUtil.ecsign(new Buffer(message, 'hex'), privKey)
|
||||
var rawMsgSig = ethUtil.bufferToHex(sigUtil.concatSig(msgSig.v, msgSig.r, msgSig.s))
|
||||
@ -70,7 +70,9 @@ class SimpleKeyring extends EventEmitter {
|
||||
/* PRIVATE METHODS */
|
||||
|
||||
_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
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -43,7 +43,9 @@ EthereumStore.prototype.addAccount = function (address) {
|
||||
self._currentState.accounts[address] = {}
|
||||
self._didUpdate()
|
||||
if (!self.currentBlockNumber) return
|
||||
self._updateAccount(address, noop)
|
||||
self._updateAccount(address, () => {
|
||||
self._didUpdate()
|
||||
})
|
||||
}
|
||||
|
||||
EthereumStore.prototype.removeAccount = function (address) {
|
||||
|
@ -51,11 +51,11 @@ PortDuplexStream.prototype._write = function (msg, encoding, cb) {
|
||||
// console.log('PortDuplexStream - sent message', msg)
|
||||
this._port.postMessage(msg)
|
||||
}
|
||||
cb()
|
||||
} catch (err) {
|
||||
// console.error(err)
|
||||
cb(new Error('PortDuplexStream - disconnected'))
|
||||
return cb(new Error('PortDuplexStream - disconnected'))
|
||||
}
|
||||
cb()
|
||||
}
|
||||
|
||||
// util
|
||||
|
@ -1,6 +1,8 @@
|
||||
const async = require('async')
|
||||
const EthQuery = require('eth-query')
|
||||
const ethUtil = require('ethereumjs-util')
|
||||
const Transaction = require('ethereumjs-tx')
|
||||
const normalize = require('./sig-util').normalize
|
||||
const BN = ethUtil.BN
|
||||
|
||||
/*
|
||||
@ -14,6 +16,7 @@ module.exports = class txProviderUtils {
|
||||
this.provider = provider
|
||||
this.query = new EthQuery(provider)
|
||||
}
|
||||
|
||||
analyzeGasUsage (txData, cb) {
|
||||
var self = this
|
||||
this.query.getBlockByNumber('latest', true, (err, block) => {
|
||||
@ -71,4 +74,59 @@ module.exports = class txProviderUtils {
|
||||
const correct = bnGas.add(gasBuffer)
|
||||
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
|
||||
}
|
||||
|
@ -45,6 +45,7 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||
getSelectedAccount: this.configManager.getSelectedAccount.bind(this.configManager),
|
||||
getGasMultiplier: this.configManager.getGasMultiplier.bind(this.configManager),
|
||||
getNetwork: this.getStateNetwork.bind(this),
|
||||
signTransaction: this.keyringController.signTransaction.bind(this.keyringController),
|
||||
provider: this.provider,
|
||||
blockTracker: this.provider,
|
||||
})
|
||||
@ -65,6 +66,7 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||
|
||||
this.ethStore.on('update', this.sendUpdate.bind(this))
|
||||
this.keyringController.on('update', this.sendUpdate.bind(this))
|
||||
this.txManager.on('update', this.sendUpdate.bind(this))
|
||||
}
|
||||
|
||||
getState () {
|
||||
@ -188,26 +190,7 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||
cb(null, result)
|
||||
},
|
||||
// tx signing
|
||||
approveTransaction: this.newUnsignedTransaction.bind(this),
|
||||
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)
|
||||
})
|
||||
},
|
||||
|
||||
processTransaction: (txParams, cb) => this.newUnapprovedTransaction(txParams, cb),
|
||||
// msg signing
|
||||
approveMessage: this.newUnsignedMessage.bind(this),
|
||||
signMessage: (...args) => {
|
||||
@ -256,24 +239,26 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||
return publicConfigStore
|
||||
}
|
||||
|
||||
newUnsignedTransaction (txParams, onTxDoneCb) {
|
||||
const txManager = this.txManager
|
||||
const err = this.enforceTxValidations(txParams)
|
||||
if (err) return onTxDoneCb(err)
|
||||
txManager.addUnapprovedTransaction(txParams, onTxDoneCb, (err, txData) => {
|
||||
if (err) return onTxDoneCb(err)
|
||||
this.sendUpdate()
|
||||
this.opts.showUnapprovedTx(txParams, txData, onTxDoneCb)
|
||||
newUnapprovedTransaction (txParams, cb) {
|
||||
const self = this
|
||||
self.txManager.addUnapprovedTransaction(txParams, (err, txMeta) => {
|
||||
if (err) return cb(err)
|
||||
self.sendUpdate()
|
||||
self.opts.showUnapprovedTx(txMeta)
|
||||
// listen for tx completion (success, fail)
|
||||
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) {
|
||||
var state = this.keyringController.getState()
|
||||
if (!state.isUnlocked) {
|
||||
|
@ -1,11 +1,11 @@
|
||||
const EventEmitter = require('events')
|
||||
const async = require('async')
|
||||
const extend = require('xtend')
|
||||
const Semaphore = require('semaphore')
|
||||
const ethUtil = require('ethereumjs-util')
|
||||
const Transaction = require('ethereumjs-tx')
|
||||
const BN = ethUtil.BN
|
||||
const BN = require('ethereumjs-util').BN
|
||||
const TxProviderUtil = require('./lib/tx-utils')
|
||||
const createId = require('./lib/random-id')
|
||||
const normalize = require('./lib/sig-util').normalize
|
||||
|
||||
module.exports = class TransactionManager extends EventEmitter {
|
||||
constructor (opts) {
|
||||
@ -20,6 +20,8 @@ module.exports = class TransactionManager extends EventEmitter {
|
||||
this.blockTracker.on('block', this.checkForTxInBlock.bind(this))
|
||||
this.getGasMultiplier = opts.getGasMultiplier
|
||||
this.getNetwork = opts.getNetwork
|
||||
this.signEthTx = opts.signTransaction
|
||||
this.nonceLock = Semaphore(1)
|
||||
}
|
||||
|
||||
getState () {
|
||||
@ -33,11 +35,12 @@ module.exports = class TransactionManager extends EventEmitter {
|
||||
|
||||
// Returns the tx list
|
||||
getTxList () {
|
||||
return this.txList
|
||||
let network = this.getNetwork()
|
||||
return this.txList.filter(txMeta => txMeta.metamaskNetworkId === network)
|
||||
}
|
||||
|
||||
// Adds a tx to the txlist
|
||||
addTx (txMeta, onTxDoneCb = warn) {
|
||||
addTx (txMeta) {
|
||||
var txList = this.getTxList()
|
||||
var txHistoryLimit = this.txHistoryLimit
|
||||
|
||||
@ -53,16 +56,11 @@ module.exports = class TransactionManager extends EventEmitter {
|
||||
txList.push(txMeta)
|
||||
|
||||
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.removeAllListeners(`${txMeta.id}:rejected`)
|
||||
onTxDoneCb(null, true)
|
||||
})
|
||||
this.once(`${txMeta.id}:rejected`, function (txId) {
|
||||
this.removeAllListeners(`${txMeta.id}:signed`)
|
||||
onTxDoneCb(null, false)
|
||||
})
|
||||
|
||||
this.emit('updateBadge')
|
||||
@ -83,6 +81,7 @@ module.exports = class TransactionManager extends EventEmitter {
|
||||
var index = txList.findIndex(txData => txData.id === txId)
|
||||
txList[index] = txMeta
|
||||
this._saveTxList(txList)
|
||||
this.emit('update')
|
||||
}
|
||||
|
||||
get unapprovedTxCount () {
|
||||
@ -93,28 +92,51 @@ module.exports = class TransactionManager extends EventEmitter {
|
||||
return this.getTxsByMetaData('status', 'signed').length
|
||||
}
|
||||
|
||||
addUnapprovedTransaction (txParams, onTxDoneCb, cb) {
|
||||
// create txData obj with parameters and meta data
|
||||
var time = (new Date()).getTime()
|
||||
var txId = createId()
|
||||
txParams.metamaskId = txId
|
||||
txParams.metamaskNetworkId = this.getNetwork()
|
||||
var txData = {
|
||||
id: txId,
|
||||
txParams: txParams,
|
||||
time: time,
|
||||
status: 'unapproved',
|
||||
gasMultiplier: this.getGasMultiplier() || 1,
|
||||
metamaskNetworkId: this.getNetwork(),
|
||||
}
|
||||
this.txProviderUtils.analyzeGasUsage(txData, this.txDidComplete.bind(this, txData, onTxDoneCb, cb))
|
||||
// calculate metadata for tx
|
||||
addUnapprovedTransaction (txParams, done) {
|
||||
let txMeta
|
||||
async.waterfall([
|
||||
// validate
|
||||
(cb) => this.txProviderUtils.validateTxParams(txParams, cb),
|
||||
// prepare txMeta
|
||||
(cb) => {
|
||||
// create txMeta obj with parameters and meta data
|
||||
let time = (new Date()).getTime()
|
||||
let txId = createId()
|
||||
txParams.metamaskId = txId
|
||||
txParams.metamaskNetworkId = this.getNetwork()
|
||||
txMeta = {
|
||||
id: txId,
|
||||
time: time,
|
||||
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) {
|
||||
if (err) return cb(err)
|
||||
this.addTx(txMeta, onTxDoneCb)
|
||||
cb(null, txMeta)
|
||||
setMaxTxCostAndFee (txMeta) {
|
||||
var txParams = txMeta.txParams
|
||||
var gasMultiplier = txMeta.gasMultiplier
|
||||
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 () {
|
||||
@ -127,8 +149,25 @@ module.exports = class TransactionManager extends EventEmitter {
|
||||
}
|
||||
|
||||
approveTransaction (txId, cb = warn) {
|
||||
this.setTxStatusSigned(txId)
|
||||
this.once(`${txId}:signingComplete`, cb)
|
||||
const self = this
|
||||
// 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) {
|
||||
@ -136,38 +175,44 @@ module.exports = class TransactionManager extends EventEmitter {
|
||||
cb()
|
||||
}
|
||||
|
||||
// formats txParams so the keyringController can sign it
|
||||
formatTxForSigining (txParams) {
|
||||
var address = txParams.from
|
||||
var metaTx = this.getTx(txParams.metamaskId)
|
||||
var gasMultiplier = metaTx.gasMultiplier
|
||||
var gasPrice = new BN(ethUtil.stripHexPrefix(txParams.gasPrice), 16)
|
||||
gasPrice = gasPrice.mul(new BN(gasMultiplier * 100, 10)).div(new BN(100, 10))
|
||||
txParams.gasPrice = ethUtil.intToHex(gasPrice.toNumber())
|
||||
fillInTxParams (txId, cb) {
|
||||
let txMeta = this.getTx(txId)
|
||||
this.txProviderUtils.fillInTxParams(txMeta.txParams, (err) => {
|
||||
if (err) return cb(err)
|
||||
this.updateTx(txMeta)
|
||||
cb()
|
||||
})
|
||||
}
|
||||
|
||||
// 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})
|
||||
signTransaction (txId, cb) {
|
||||
let txMeta = this.getTx(txId)
|
||||
let txParams = txMeta.txParams
|
||||
let fromAddress = txParams.from
|
||||
let ethTx = this.txProviderUtils.buildEthTxFromParams(txParams, txMeta.gasMultiplier)
|
||||
this.signEthTx(ethTx, fromAddress).then(() => {
|
||||
this.updateTxAsSigned(txMeta.id, ethTx)
|
||||
cb(null, ethUtil.bufferToHex(ethTx.serialize()))
|
||||
}).catch((err) => {
|
||||
cb(err)
|
||||
})
|
||||
}
|
||||
|
||||
publishTransaction (txId, rawTx, cb) {
|
||||
this.txProviderUtils.publishTransaction(rawTx, (err) => {
|
||||
if (err) return cb(err)
|
||||
this.setTxStatusSubmitted(txId)
|
||||
cb()
|
||||
})
|
||||
}
|
||||
|
||||
// receives a signed tx object and updates the tx hash
|
||||
// and pass it to the cb to be sent off
|
||||
resolveSignedTransaction ({tx, txId, cb = warn}) {
|
||||
updateTxAsSigned (txId, ethTx) {
|
||||
// Add the tx hash to the persisted meta-tx object
|
||||
var txHash = ethUtil.bufferToHex(tx.hash())
|
||||
var metaTx = this.getTx(txId)
|
||||
metaTx.hash = txHash
|
||||
this.updateTx(metaTx)
|
||||
var rawTx = ethUtil.bufferToHex(tx.serialize())
|
||||
return Promise.resolve(rawTx)
|
||||
|
||||
let txHash = ethUtil.bufferToHex(ethTx.hash())
|
||||
let txMeta = this.getTx(txId)
|
||||
txMeta.hash = txHash
|
||||
this.updateTx(txMeta)
|
||||
this.setTxStatusSigned(txMeta.id)
|
||||
}
|
||||
|
||||
/*
|
||||
@ -212,23 +257,35 @@ module.exports = class TransactionManager extends EventEmitter {
|
||||
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'.
|
||||
setTxStatusSigned (txId) {
|
||||
this._setTxStatus(txId, 'signed')
|
||||
this.emit('updateBadge')
|
||||
}
|
||||
|
||||
// should update the status of the tx to 'rejected'.
|
||||
setTxStatusRejected (txId) {
|
||||
this._setTxStatus(txId, 'rejected')
|
||||
this.emit('updateBadge')
|
||||
// should update the status of the tx to 'submitted'.
|
||||
setTxStatusSubmitted (txId) {
|
||||
this._setTxStatus(txId, 'submitted')
|
||||
}
|
||||
|
||||
// should update the status of the tx to 'confirmed'.
|
||||
setTxStatusConfirmed (txId) {
|
||||
this._setTxStatus(txId, 'confirmed')
|
||||
}
|
||||
|
||||
setTxStatusFailed (txId) {
|
||||
this._setTxStatus(txId, 'failed')
|
||||
}
|
||||
|
||||
// merges txParams obj onto txData.txParams
|
||||
// use extend to ensure that all fields are filled
|
||||
updateTxParams (txId, txParams) {
|
||||
@ -240,19 +297,31 @@ module.exports = class TransactionManager extends EventEmitter {
|
||||
// checks if a signed tx is in a block and
|
||||
// if included sets the tx status as 'confirmed'
|
||||
checkForTxInBlock () {
|
||||
var signedTxList = this.getFilteredTxList({status: 'signed', err: undefined})
|
||||
var signedTxList = this.getFilteredTxList({status: 'signed'})
|
||||
if (!signedTxList.length) return
|
||||
signedTxList.forEach((tx) => {
|
||||
var txHash = tx.hash
|
||||
var txId = tx.id
|
||||
if (!txHash) return
|
||||
this.txProviderUtils.query.getTransactionByHash(txHash, (err, txMeta) => {
|
||||
if (err || !txMeta) {
|
||||
tx.err = err || 'Tx could possibly have not been submitted'
|
||||
this.updateTx(tx)
|
||||
return txMeta ? console.error(err) : console.debug(`txMeta is ${txMeta} for:`, tx)
|
||||
signedTxList.forEach((txMeta) => {
|
||||
var txHash = txMeta.hash
|
||||
var txId = txMeta.id
|
||||
if (!txHash) {
|
||||
txMeta.err = {
|
||||
errCode: 'No hash was provided',
|
||||
message: 'We had an error while submitting this transaction, please try again.',
|
||||
}
|
||||
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)
|
||||
}
|
||||
})
|
||||
@ -266,6 +335,7 @@ module.exports = class TransactionManager extends EventEmitter {
|
||||
// should set the status in txData
|
||||
// - `'unapproved'` the user has not responded
|
||||
// - `'rejected'` the user has responded no!
|
||||
// - `'approved'` the user has approved the tx
|
||||
// - `'signed'` the tx is signed
|
||||
// - `'submitted'` the tx is sent to a server
|
||||
// - `'confirmed'` the tx has been included in a block.
|
||||
@ -273,7 +343,11 @@ module.exports = class TransactionManager extends EventEmitter {
|
||||
var txMeta = this.getTx(txId)
|
||||
txMeta.status = status
|
||||
this.emit(`${txMeta.id}:${status}`, txId)
|
||||
if (status === 'submitted' || status === 'rejected') {
|
||||
this.emit(`${txMeta.id}:finished`, status)
|
||||
}
|
||||
this.updateTx(txMeta)
|
||||
this.emit('updateBadge')
|
||||
}
|
||||
|
||||
// Saves the new/updated txList.
|
||||
|
@ -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
|
||||
|
@ -89,13 +89,14 @@
|
||||
"redux-logger": "^2.3.1",
|
||||
"redux-thunk": "^1.0.2",
|
||||
"sandwich-expando": "^1.0.5",
|
||||
"semaphore": "^1.0.5",
|
||||
"textarea-caret": "^3.0.1",
|
||||
"three.js": "^0.73.2",
|
||||
"through2": "^2.0.1",
|
||||
"valid-url": "^1.0.9",
|
||||
"vreme": "^3.0.2",
|
||||
"web3": "0.17.0-beta",
|
||||
"web3-provider-engine": "^8.2.0",
|
||||
"web3-provider-engine": "^8.4.0",
|
||||
"web3-stream-provider": "^2.0.6",
|
||||
"xtend": "^4.0.1"
|
||||
},
|
||||
|
@ -4,7 +4,7 @@ var linkGen = require('../../ui/lib/explorer-link')
|
||||
describe('explorer-link', 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')
|
||||
})
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
const assert = require('assert')
|
||||
const extend = require('xtend')
|
||||
const ethUtil = require('ethereumjs-util')
|
||||
const SimpleKeyring = require('../../../app/scripts/keyrings/simple')
|
||||
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('with no arguments', function() {
|
||||
it('creates a single wallet', function() {
|
||||
@ -72,14 +91,10 @@ describe('simple-keyring', function() {
|
||||
it('calls getAddress on each wallet', function(done) {
|
||||
|
||||
// Push a mock wallet
|
||||
const desiredOutput = 'foo'
|
||||
const desiredOutput = '0x18a3462427bcc9133bb46e88bcbe39cd7ef0e761'
|
||||
keyring.wallets.push({
|
||||
getAddress() {
|
||||
return {
|
||||
toString() {
|
||||
return desiredOutput
|
||||
}
|
||||
}
|
||||
return ethUtil.toBuffer(desiredOutput)
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -25,24 +25,6 @@ describe('MetaMaskController', function() {
|
||||
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 NoticeController = require('../../app/scripts/notice-controller')
|
||||
const STORAGE_KEY = 'metamask-persistance-key'
|
||||
// Hacking localStorage support into JSDom
|
||||
window.localStorage = {}
|
||||
|
||||
describe('notice-controller', function() {
|
||||
var noticeController
|
||||
|
||||
beforeEach(function() {
|
||||
// simple localStorage polyfill
|
||||
window.localStorage = {}
|
||||
if (window.localStorage.clear) window.localStorage.clear()
|
||||
let configManager = configManagerGen()
|
||||
noticeController = new NoticeController({
|
||||
configManager: configManager,
|
||||
|
@ -15,6 +15,28 @@ describe('Transaction Manager', function() {
|
||||
provider: "testnet",
|
||||
txHistoryLimit: 10,
|
||||
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() {
|
||||
it('saves the submitted data to the tx list', function() {
|
||||
var target = [{ foo: 'bar' }]
|
||||
var target = [{ foo: 'bar', metamaskNetworkId: 'unit test' }]
|
||||
txManager._saveTxList(target)
|
||||
var result = txManager.getTxList()
|
||||
assert.equal(result[0].foo, 'bar')
|
||||
@ -40,7 +62,7 @@ describe('Transaction Manager', function() {
|
||||
|
||||
describe('#addTx', 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)
|
||||
var result = txManager.getTxList()
|
||||
assert.ok(Array.isArray(result))
|
||||
@ -51,7 +73,7 @@ describe('Transaction Manager', function() {
|
||||
it('cuts off early txs beyond a limit', function() {
|
||||
const limit = txManager.txHistoryLimit
|
||||
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)
|
||||
}
|
||||
var result = txManager.getTxList()
|
||||
@ -59,10 +81,10 @@ describe('Transaction Manager', function() {
|
||||
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
|
||||
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)
|
||||
}
|
||||
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() {
|
||||
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)
|
||||
const limit = txManager.txHistoryLimit
|
||||
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)
|
||||
}
|
||||
var result = txManager.getTxList()
|
||||
@ -88,7 +110,7 @@ describe('Transaction Manager', function() {
|
||||
|
||||
describe('#setTxStatusSigned', 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.setTxStatusSigned(1)
|
||||
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) => {
|
||||
this.timeout(10000)
|
||||
var tx = { id: 1, status: 'unapproved' }
|
||||
let onTxDoneCb = function (err, txId) {
|
||||
var tx = { id: 1, status: 'unapproved', metamaskNetworkId: 'unit test' }
|
||||
let onTxDoneCb = function () {
|
||||
assert(true, 'event listener has been triggered and onTxDoneCb executed')
|
||||
done()
|
||||
}
|
||||
txManager.addTx(tx, onTxDoneCb)
|
||||
txManager.addTx(tx)
|
||||
txManager.on('1:signed', onTxDoneCb)
|
||||
txManager.setTxStatusSigned(1)
|
||||
})
|
||||
})
|
||||
|
||||
describe('#setTxStatusRejected', function() {
|
||||
it('sets the tx status to rejected', function() {
|
||||
var tx = { id: 1, status: 'unapproved' }
|
||||
txManager.addTx(tx, onTxDoneCb)
|
||||
var tx = { id: 1, status: 'unapproved', metamaskNetworkId: 'unit test' }
|
||||
txManager.addTx(tx)
|
||||
txManager.setTxStatusRejected(1)
|
||||
var result = txManager.getTxList()
|
||||
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) => {
|
||||
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) {
|
||||
assert(true, 'event listener has been triggered and onTxDoneCb executed')
|
||||
done()
|
||||
}
|
||||
txManager.addTx(tx, onTxDoneCb)
|
||||
txManager.on('1:rejected', onTxDoneCb)
|
||||
txManager.setTxStatusRejected(1)
|
||||
})
|
||||
|
||||
@ -135,9 +159,9 @@ describe('Transaction Manager', function() {
|
||||
|
||||
describe('#updateTx', function() {
|
||||
it('replaces the tx with the same id', function() {
|
||||
txManager.addTx({ id: '1', status: 'unapproved' }, onTxDoneCb)
|
||||
txManager.addTx({ id: '2', status: 'confirmed' }, onTxDoneCb)
|
||||
txManager.updateTx({ id: '1', status: 'blah', hash: 'foo' })
|
||||
txManager.addTx({ id: '1', status: 'unapproved', metamaskNetworkId: 'unit test' }, onTxDoneCb)
|
||||
txManager.addTx({ id: '2', status: 'confirmed', metamaskNetworkId: 'unit test' }, onTxDoneCb)
|
||||
txManager.updateTx({ id: '1', status: 'blah', hash: 'foo', metamaskNetworkId: 'unit test' })
|
||||
var result = txManager.getTx('1')
|
||||
assert.equal(result.hash, 'foo')
|
||||
})
|
||||
@ -145,8 +169,8 @@ describe('Transaction Manager', function() {
|
||||
|
||||
describe('#getUnapprovedTxList', function() {
|
||||
it('returns unapproved txs in a hash', function() {
|
||||
txManager.addTx({ id: '1', status: 'unapproved' }, onTxDoneCb)
|
||||
txManager.addTx({ id: '2', status: 'confirmed' }, onTxDoneCb)
|
||||
txManager.addTx({ id: '1', status: 'unapproved', metamaskNetworkId: 'unit test' }, onTxDoneCb)
|
||||
txManager.addTx({ id: '2', status: 'confirmed', metamaskNetworkId: 'unit test' }, onTxDoneCb)
|
||||
let result = txManager.getUnapprovedTxList()
|
||||
assert.equal(typeof result, 'object')
|
||||
assert.equal(result['1'].status, 'unapproved')
|
||||
@ -156,8 +180,8 @@ describe('Transaction Manager', function() {
|
||||
|
||||
describe('#getTx', function() {
|
||||
it('returns a tx with the requested id', function() {
|
||||
txManager.addTx({ id: '1', status: 'unapproved' }, onTxDoneCb)
|
||||
txManager.addTx({ id: '2', status: 'confirmed' }, onTxDoneCb)
|
||||
txManager.addTx({ id: '1', status: 'unapproved', metamaskNetworkId: 'unit test' }, onTxDoneCb)
|
||||
txManager.addTx({ id: '2', status: 'confirmed', metamaskNetworkId: 'unit test' }, onTxDoneCb)
|
||||
assert.equal(txManager.getTx('1').status, 'unapproved')
|
||||
assert.equal(txManager.getTx('2').status, 'confirmed')
|
||||
})
|
||||
@ -171,6 +195,7 @@ describe('Transaction Manager', function() {
|
||||
let everyOther = i % 2
|
||||
txManager.addTx({ id: i,
|
||||
status: everyOther ? 'unapproved' : 'confirmed',
|
||||
metamaskNetworkId: 'unit test',
|
||||
txParams: {
|
||||
from: everyOther ? 'foop' : 'zoop',
|
||||
to: everyOther ? 'zoop' : 'foop',
|
||||
|
@ -26,11 +26,10 @@ function mapStateToProps (state) {
|
||||
accounts: state.metamask.accounts,
|
||||
address: state.metamask.selectedAccount,
|
||||
accountDetail: state.appState.accountDetail,
|
||||
transactions: state.metamask.transactions,
|
||||
network: state.metamask.network,
|
||||
unconfTxs: valuesFor(state.metamask.unconfTxs),
|
||||
unconfMsgs: valuesFor(state.metamask.unconfMsgs),
|
||||
shapeShiftTxList: state.metamask.shapeShiftTxList,
|
||||
transactions: state.metamask.selectedAccountTxList || [],
|
||||
}
|
||||
}
|
||||
|
||||
@ -248,20 +247,10 @@ AccountDetailScreen.prototype.subview = function () {
|
||||
}
|
||||
|
||||
AccountDetailScreen.prototype.transactionList = function () {
|
||||
const { transactions, unconfTxs, 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)
|
||||
|
||||
const {transactions, unconfMsgs, address, network, shapeShiftTxList } = this.props
|
||||
return h(TransactionList, {
|
||||
txsToRender,
|
||||
transactions: transactions.sort((a, b) => b.time - a.time),
|
||||
network,
|
||||
unconfTxs,
|
||||
unconfMsgs,
|
||||
address,
|
||||
shapeShiftTxList,
|
||||
|
@ -263,9 +263,7 @@ function showInfoPage () {
|
||||
}
|
||||
|
||||
function setSelectedAccount (address) {
|
||||
return (dispatch) => {
|
||||
background.setSelectedAccount(address)
|
||||
}
|
||||
return callBackgroundThenUpdate(background.setSelectedAccount, address)
|
||||
}
|
||||
|
||||
function setCurrentFiat (fiat) {
|
||||
@ -457,15 +455,16 @@ function lockMetamask () {
|
||||
function showAccountDetail (address) {
|
||||
return (dispatch) => {
|
||||
dispatch(actions.showLoadingIndication())
|
||||
background.setSelectedAccount(address, (err, address) => {
|
||||
background.setSelectedAccount(address, (err, newState) => {
|
||||
dispatch(actions.hideLoadingIndication())
|
||||
if (err) {
|
||||
return dispatch(actions.displayWarning(err.message))
|
||||
}
|
||||
|
||||
dispatch(actions.updateMetamaskState(newState))
|
||||
dispatch({
|
||||
type: actions.SHOW_ACCOUNT_DETAIL,
|
||||
value: address,
|
||||
value: newState.selectedAccount,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
@ -72,7 +72,7 @@ CoinbaseForm.prototype.render = function () {
|
||||
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
|
||||
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) {
|
||||
amount = parseFloat(amount)
|
||||
if (amount) {
|
||||
if (amount <= 5 && amount > 0) {
|
||||
if (amount <= 15 && amount > 0) {
|
||||
return {
|
||||
valid: true,
|
||||
}
|
||||
} else if (amount > 5) {
|
||||
} else if (amount > 15) {
|
||||
return {
|
||||
valid: false,
|
||||
message: 'The amount can not be greater then $5',
|
||||
message: 'The amount can not be greater then $15',
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
|
@ -7,8 +7,6 @@ const EthBalance = require('./eth-balance')
|
||||
const util = require('../util')
|
||||
const addressSummary = util.addressSummary
|
||||
const nameForAddress = require('../../lib/contract-namer')
|
||||
const ethUtil = require('ethereumjs-util')
|
||||
const BN = ethUtil.BN
|
||||
|
||||
module.exports = PendingTxDetails
|
||||
|
||||
@ -29,15 +27,9 @@ PTXP.render = function () {
|
||||
var account = props.accounts[address]
|
||||
var balance = account ? account.balance : '0x0'
|
||||
|
||||
var gasMultiplier = txData.gasMultiplier
|
||||
var gasCost = new BN(ethUtil.stripHexPrefix(txParams.gas || txData.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)
|
||||
var txFee = txData.txFee || ''
|
||||
var maxCost = txData.maxCost || ''
|
||||
var dataLength = txParams.data ? (txParams.data.length - 2) / 2 : 0
|
||||
|
||||
var imageify = props.imageifyIdenticons === undefined ? true : props.imageifyIdenticons
|
||||
|
||||
return (
|
||||
|
@ -13,13 +13,40 @@ function TransactionIcon () {
|
||||
|
||||
TransactionIcon.prototype.render = function () {
|
||||
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') {
|
||||
return h('i.fa.fa-exclamation-triangle.fa-lg.warning', {
|
||||
style: {
|
||||
width: '24px',
|
||||
},
|
||||
})
|
||||
case 'rejected':
|
||||
return h('i.fa.fa-exclamation-triangle.fa-lg.warning', {
|
||||
style: {
|
||||
width: '24px',
|
||||
},
|
||||
})
|
||||
|
||||
case 'failed':
|
||||
return h('i.fa.fa-exclamation-triangle.fa-lg.error', {
|
||||
style: {
|
||||
width: '24px',
|
||||
},
|
||||
})
|
||||
|
||||
case 'signed':
|
||||
return h('i.fa.fa-ellipsis-h', {
|
||||
style: {
|
||||
fontSize: '27px',
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
if (isMsg) {
|
||||
|
@ -8,6 +8,7 @@ const explorerLink = require('../../lib/explorer-link')
|
||||
const CopyButton = require('./copyButton')
|
||||
const vreme = new (require('vreme'))
|
||||
const extension = require('../../../app/scripts/lib/extension')
|
||||
const Tooltip = require('./tooltip')
|
||||
|
||||
const TransactionIcon = require('./transaction-list-item-icon')
|
||||
const ShiftListItem = require('./shift-list-item')
|
||||
@ -27,7 +28,7 @@ TransactionListItem.prototype.render = function () {
|
||||
|
||||
let isLinkable = false
|
||||
const numericNet = parseInt(network)
|
||||
isLinkable = numericNet === 1 || numericNet === 2
|
||||
isLinkable = numericNet === 1 || numericNet === 3
|
||||
|
||||
var isMsg = ('msgParams' in transaction)
|
||||
var isTx = ('txParams' in transaction)
|
||||
@ -41,7 +42,6 @@ TransactionListItem.prototype.render = function () {
|
||||
}
|
||||
|
||||
const isClickable = ('hash' in transaction && isLinkable) || isPending
|
||||
|
||||
return (
|
||||
h(`.transaction-list-item.flex-row.flex-space-between${isClickable ? '.pointer' : ''}`, {
|
||||
onClick: (event) => {
|
||||
@ -59,11 +59,7 @@ TransactionListItem.prototype.render = function () {
|
||||
}, [
|
||||
|
||||
h('.identicon-wrapper.flex-column.flex-center.select-none', [
|
||||
transaction.status === 'unapproved' ? h('i.fa.fa-ellipsis-h', {
|
||||
style: {
|
||||
fontSize: '27px',
|
||||
},
|
||||
}) : h('.pop-hover', {
|
||||
h('.pop-hover', {
|
||||
onClick: (event) => {
|
||||
event.stopPropagation()
|
||||
if (!isTx || isPending) return
|
||||
@ -139,7 +135,14 @@ function failIfFailed (transaction) {
|
||||
if (transaction.status === 'rejected') {
|
||||
return h('span.error', ' (Rejected)')
|
||||
}
|
||||
if (transaction.status === 'failed') {
|
||||
return h('span.error', ' (Failed)')
|
||||
if (transaction.err) {
|
||||
|
||||
return h(Tooltip, {
|
||||
title: transaction.err.message,
|
||||
position: 'bottom',
|
||||
}, [
|
||||
h('span.error', ' (Failed)'),
|
||||
])
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -13,12 +13,13 @@ function TransactionList () {
|
||||
}
|
||||
|
||||
TransactionList.prototype.render = function () {
|
||||
const { txsToRender, network, unconfMsgs } = this.props
|
||||
const { transactions, network, unconfMsgs } = this.props
|
||||
|
||||
var shapeShiftTxList
|
||||
if (network === '1') {
|
||||
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)
|
||||
|
||||
return (
|
||||
@ -55,8 +56,8 @@ TransactionList.prototype.render = function () {
|
||||
},
|
||||
}, [
|
||||
|
||||
transactions.length
|
||||
? transactions.map((transaction, i) => {
|
||||
txsToRender.length
|
||||
? txsToRender.map((transaction, i) => {
|
||||
let key
|
||||
switch (transaction.key) {
|
||||
case 'shapeshift':
|
||||
|
@ -41,11 +41,13 @@ ConfirmTxScreen.prototype.render = function () {
|
||||
var provider = state.provider
|
||||
var unconfTxs = state.unconfTxs
|
||||
var unconfMsgs = state.unconfMsgs
|
||||
|
||||
var unconfTxList = txHelper(unconfTxs, unconfMsgs, network)
|
||||
var index = state.index !== undefined ? state.index : 0
|
||||
var txData = unconfTxList[index] || unconfTxList[0] || {}
|
||||
var txParams = txData.txParams || {}
|
||||
var index = state.index !== undefined && unconfTxList[index] ? state.index : 0
|
||||
var txData = unconfTxList[index] || {}
|
||||
var txParams = txData.params || {}
|
||||
var isNotification = isPopupOrNotification() === 'notification'
|
||||
if (unconfTxList.length === 0) return null
|
||||
|
||||
return (
|
||||
|
||||
@ -115,27 +117,24 @@ ConfirmTxScreen.prototype.render = function () {
|
||||
}
|
||||
|
||||
function currentTxView (opts) {
|
||||
if ('txParams' in opts.txData) {
|
||||
const { txData } = opts
|
||||
const { txParams, msgParams } = txData
|
||||
|
||||
if (txParams) {
|
||||
// This is a pending transaction
|
||||
return h(PendingTx, opts)
|
||||
} else if ('msgParams' in opts.txData) {
|
||||
} else if (msgParams) {
|
||||
// This is a pending message to sign
|
||||
return h(PendingMsg, opts)
|
||||
}
|
||||
}
|
||||
ConfirmTxScreen.prototype.checkBalanceAgainstTx = function (txData) {
|
||||
if (!txData.txParams) return false
|
||||
var state = this.props
|
||||
|
||||
var txParams = txData.txParams || {}
|
||||
var address = txParams.from || state.selectedAccount
|
||||
var address = txData.txParams.from || state.selectedAccount
|
||||
var account = state.accounts[address]
|
||||
var balance = account ? account.balance : '0x0'
|
||||
|
||||
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 maxCost = new BN(txData.maxCost)
|
||||
|
||||
var balanceBn = new BN(ethUtil.stripHexPrefix(balance), 16)
|
||||
return maxCost.gt(balanceBn)
|
||||
|
@ -446,7 +446,7 @@ function reduceApp (state, action) {
|
||||
},
|
||||
buyView: {
|
||||
subview: 'buyForm',
|
||||
amount: '5.00',
|
||||
amount: '15.00',
|
||||
buyAddress: action.value,
|
||||
formView: {
|
||||
coinbase: true,
|
||||
|
@ -5,7 +5,7 @@ module.exports = function (hash, network) {
|
||||
case 1: // main net
|
||||
prefix = ''
|
||||
break
|
||||
case 2: // morden test net
|
||||
case 3: // morden test net
|
||||
prefix = 'testnet.'
|
||||
break
|
||||
default:
|
||||
|
Loading…
Reference in New Issue
Block a user