1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-12-23 09:52:26 +01:00

Merge branch 'master' into networkController

This commit is contained in:
frankiebee 2017-05-22 19:48:10 -04:00
commit 13a41f3129
43 changed files with 823 additions and 5965 deletions

View File

@ -1 +1,4 @@
{ "presets": ["es2015"] } {
"presets": ["es2015"],
"plugins": ["transform-runtime"]
}

View File

@ -1,7 +1,7 @@
{ {
"parserOptions": { "parserOptions": {
"sourceType": "module", "sourceType": "module",
"ecmaVersion": 6, "ecmaVersion": 2017,
"ecmaFeatures": { "ecmaFeatures": {
"experimentalObjectRestSpread": true, "experimentalObjectRestSpread": true,
"impliedStrict": true, "impliedStrict": true,

View File

@ -2,6 +2,17 @@
## Current Master ## Current Master
## 3.6.5 2017-5-17
- Fix bug where edited gas parameters would not take effect.
- Trim currency list.
- Fix event filter bug introduced by newer versions of Geth.
- Fix bug where decimals in gas inputs could result in strange values.
## 3.6.4 2017-5-8
- Fix main-net ENS resolution.
## 3.6.3 2017-5-8 ## 3.6.3 2017-5-8
- Fix bug that could stop newer versions of Geth from working with MetaMask. - Fix bug that could stop newer versions of Geth from working with MetaMask.

File diff suppressed because one or more lines are too long

View File

@ -1,7 +1,7 @@
{ {
"name": "MetaMask", "name": "MetaMask",
"short_name": "Metamask", "short_name": "Metamask",
"version": "3.6.3", "version": "3.6.5",
"manifest_version": 2, "manifest_version": 2,
"author": "https://metamask.io", "author": "https://metamask.io",
"description": "Ethereum Browser Extension", "description": "Ethereum Browser Extension",

View File

@ -1,6 +1,5 @@
const urlUtil = require('url') const urlUtil = require('url')
const endOfStream = require('end-of-stream') const endOfStream = require('end-of-stream')
const asyncQ = require('async-q')
const pipe = require('pump') const pipe = require('pump')
const LocalStorageStore = require('obs-store/lib/localStorage') const LocalStorageStore = require('obs-store/lib/localStorage')
const storeTransform = require('obs-store/lib/transform') const storeTransform = require('obs-store/lib/transform')
@ -30,34 +29,29 @@ let popupIsOpen = false
const diskStore = new LocalStorageStore({ storageKey: STORAGE_KEY }) const diskStore = new LocalStorageStore({ storageKey: STORAGE_KEY })
// initialization flow // initialization flow
asyncQ.waterfall([ initialize().catch(console.error)
() => loadStateFromPersistence(),
(initState) => setupController(initState), async function initialize() {
]) const initState = await loadStateFromPersistence()
.then(() => console.log('MetaMask initialization complete.')) await setupController(initState)
.catch((err) => { console.error(err) }) console.log('MetaMask initialization complete.')
}
// //
// State and Persistence // State and Persistence
// //
function loadStateFromPersistence () { async function loadStateFromPersistence () {
// migrations // migrations
const migrator = new Migrator({ migrations }) const migrator = new Migrator({ migrations })
const initialState = migrator.generateInitialState(firstTimeState) // read from disk
return asyncQ.waterfall([ let versionedData = diskStore.getState() || migrator.generateInitialState(firstTimeState)
// read from disk // migrate data
() => Promise.resolve(diskStore.getState() || initialState), versionedData = await migrator.migrateData(versionedData)
// migrate data // write to disk
(versionedData) => migrator.migrateData(versionedData), diskStore.putState(versionedData)
// write to disk // return just the data
(versionedData) => { return versionedData.data
diskStore.putState(versionedData)
return Promise.resolve(versionedData)
},
// resolve to just data
(versionedData) => Promise.resolve(versionedData.data),
])
} }
function setupController (initState) { function setupController (initState) {
@ -120,13 +114,13 @@ function setupController (initState) {
// //
updateBadge() updateBadge()
controller.txManager.on('updateBadge', updateBadge) controller.txController.on('updateBadge', updateBadge)
controller.messageManager.on('updateBadge', updateBadge) controller.messageManager.on('updateBadge', updateBadge)
// plugin badge text // plugin badge text
function updateBadge () { function updateBadge () {
var label = '' var label = ''
var unapprovedTxCount = controller.txManager.unapprovedTxCount var unapprovedTxCount = controller.txController.unapprovedTxCount
var unapprovedMsgCount = controller.messageManager.unapprovedMsgCount var unapprovedMsgCount = controller.messageManager.unapprovedMsgCount
var count = unapprovedTxCount + unapprovedMsgCount var count = unapprovedTxCount + unapprovedMsgCount
if (count) { if (count) {

View File

@ -1,17 +1,14 @@
const MAINET_RPC_URL = 'https://mainnet.infura.io/metamask' const MAINET_RPC_URL = 'https://mainnet.infura.io/metamask'
const TESTNET_RPC_URL = 'https://ropsten.infura.io/metamask' const ROPSTEN_RPC_URL = 'https://ropsten.infura.io/metamask'
const KOVAN_RPC_URL = 'https://kovan.infura.io/metamask' const KOVAN_RPC_URL = 'https://kovan.infura.io/metamask'
const RINKEBY_RPC_URL = 'https://rinkeby.infura.io/metamask' const RINKEBY_RPC_URL = 'https://rinkeby.infura.io/metamask'
const DEFAULT_RPC_URL = TESTNET_RPC_URL
global.METAMASK_DEBUG = 'GULP_METAMASK_DEBUG' global.METAMASK_DEBUG = 'GULP_METAMASK_DEBUG'
module.exports = { module.exports = {
network: { network: {
default: DEFAULT_RPC_URL,
mainnet: MAINET_RPC_URL, mainnet: MAINET_RPC_URL,
testnet: TESTNET_RPC_URL, ropsten: ROPSTEN_RPC_URL,
morden: TESTNET_RPC_URL,
kovan: KOVAN_RPC_URL, kovan: KOVAN_RPC_URL,
rinkeby: RINKEBY_RPC_URL, rinkeby: RINKEBY_RPC_URL,
}, },

View File

@ -4,8 +4,9 @@ const extend = require('xtend')
const Semaphore = require('semaphore') const Semaphore = require('semaphore')
const ObservableStore = require('obs-store') const ObservableStore = require('obs-store')
const ethUtil = require('ethereumjs-util') const ethUtil = require('ethereumjs-util')
const TxProviderUtil = require('./lib/tx-utils') const EthQuery = require('eth-query')
const createId = require('./lib/random-id') const TxProviderUtil = require('../lib/tx-utils')
const createId = require('../lib/random-id')
module.exports = class TransactionManager extends EventEmitter { module.exports = class TransactionManager extends EventEmitter {
constructor (opts) { constructor (opts) {

View File

@ -5,7 +5,7 @@
module.exports = { module.exports = {
config: { config: {
provider: { provider: {
type: 'testnet', type: 'rinkeby',
}, },
}, },
} }

View File

@ -1,6 +1,14 @@
const ethUtil = require('ethereumjs-util') const ethUtil = require('ethereumjs-util')
const normalize = require('eth-sig-util').normalize const normalize = require('eth-sig-util').normalize
<<<<<<< HEAD
=======
const MAINNET_RPC = MetamaskConfig.network.mainnet
const ROPSTEN_RPC = MetamaskConfig.network.ropsten
const KOVAN_RPC = MetamaskConfig.network.kovan
const RINKEBY_RPC = MetamaskConfig.network.rinkeby
>>>>>>> master
/* The config-manager is a convenience object /* The config-manager is a convenience object
* wrapping a pojo-migrator. * wrapping a pojo-migrator.
* *
@ -101,6 +109,32 @@ ConfigManager.prototype.getSeedWords = function () {
return data.seedWords return data.seedWords
} }
ConfigManager.prototype.getCurrentRpcAddress = function () {
var provider = this.getProvider()
if (!provider) return null
switch (provider.type) {
case 'mainnet':
return MAINNET_RPC
case 'ropsten':
return ROPSTEN_RPC
case 'kovan':
return KOVAN_RPC
case 'rinkeby':
return RINKEBY_RPC
default:
return provider && provider.rpcTarget ? provider.rpcTarget : RINKEBY_RPC
}
}
//
// Tx
//
ConfigManager.prototype.getTxList = function () { ConfigManager.prototype.getTxList = function () {
var data = this.getData() var data = this.getData()
if (data.transactions !== undefined) { if (data.transactions !== undefined) {

View File

@ -1,42 +1,35 @@
const asyncQ = require('async-q')
class Migrator { class Migrator {
constructor (opts = {}) { constructor (opts = {}) {
const migrations = opts.migrations || [] const migrations = opts.migrations || []
// sort migrations by version
this.migrations = migrations.sort((a, b) => a.version - b.version) this.migrations = migrations.sort((a, b) => a.version - b.version)
// grab migration with highest version
const lastMigration = this.migrations.slice(-1)[0] const lastMigration = this.migrations.slice(-1)[0]
// use specified defaultVersion or highest migration version // use specified defaultVersion or highest migration version
this.defaultVersion = opts.defaultVersion || (lastMigration && lastMigration.version) || 0 this.defaultVersion = opts.defaultVersion || (lastMigration && lastMigration.version) || 0
} }
// run all pending migrations on meta in place // run all pending migrations on meta in place
migrateData (versionedData = this.generateInitialState()) { async migrateData (versionedData = this.generateInitialState()) {
const remaining = this.migrations.filter(migrationIsPending) const pendingMigrations = this.migrations.filter(migrationIsPending)
return ( for (let index in pendingMigrations) {
asyncQ.eachSeries(remaining, (migration) => this.runMigration(versionedData, migration)) let migration = pendingMigrations[index]
.then(() => versionedData) versionedData = await migration.migrate(versionedData)
) if (!versionedData.data) throw new Error('Migrator - migration returned empty data')
if (versionedData.version !== undefined && versionedData.meta.version !== migration.version) throw new Error('Migrator - Migration did not update version number correctly')
}
// migration is "pending" if hit has a higher return versionedData
// migration is "pending" if it has a higher
// version number than currentVersion // version number than currentVersion
function migrationIsPending (migration) { function migrationIsPending (migration) {
return migration.version > versionedData.meta.version return migration.version > versionedData.meta.version
} }
} }
runMigration (versionedData, migration) {
return (
migration.migrate(versionedData)
.then((versionedData) => {
if (!versionedData.data) return Promise.reject(new Error('Migrator - Migration returned empty data'))
if (migration.version !== undefined && versionedData.meta.version !== migration.version) return Promise.reject(new Error('Migrator - Migration did not update version number correctly'))
return Promise.resolve(versionedData)
})
)
}
generateInitialState (initState) { generateInitialState (initState) {
return { return {
meta: { meta: {

View File

@ -17,7 +17,7 @@ const ShapeShiftController = require('./controllers/shapeshift')
const AddressBookController = require('./controllers/address-book') const AddressBookController = require('./controllers/address-book')
const MessageManager = require('./lib/message-manager') const MessageManager = require('./lib/message-manager')
const PersonalMessageManager = require('./lib/personal-message-manager') const PersonalMessageManager = require('./lib/personal-message-manager')
const TxManager = require('./transaction-manager') const TransactionController = require('./controllers/transactions')
const ConfigManager = require('./lib/config-manager') const ConfigManager = require('./lib/config-manager')
const autoFaucet = require('./lib/auto-faucet') const autoFaucet = require('./lib/auto-faucet')
const nodeify = require('./lib/nodeify') const nodeify = require('./lib/nodeify')
@ -89,8 +89,8 @@ module.exports = class MetamaskController extends EventEmitter {
}, this.keyringController) }, this.keyringController)
// tx mgmt // tx mgmt
this.txManager = new TxManager({ this.txController = new TransactionController({
initState: initState.TransactionManager, initState: initState.TransactionController || initState.TransactionManager,
networkStore: this.networkController.networkStore, networkStore: this.networkController.networkStore,
preferencesStore: this.preferencesController.store, preferencesStore: this.preferencesController.store,
txHistoryLimit: 40, txHistoryLimit: 40,
@ -144,8 +144,8 @@ module.exports = class MetamaskController extends EventEmitter {
this.publicConfigStore = this.initPublicConfigStore() this.publicConfigStore = this.initPublicConfigStore()
// manual disk state subscriptions // manual disk state subscriptions
this.txManager.store.subscribe((state) => { this.txController.store.subscribe((state) => {
this.store.updateState({ TransactionManager: state }) this.store.updateState({ TransactionController: state })
}) })
this.keyringController.store.subscribe((state) => { this.keyringController.store.subscribe((state) => {
this.store.updateState({ KeyringController: state }) this.store.updateState({ KeyringController: state })
@ -172,7 +172,7 @@ module.exports = class MetamaskController extends EventEmitter {
// manual mem state subscriptions // manual mem state subscriptions
this.networkController.subscribe(this.sendUpdate.bind(this)) this.networkController.subscribe(this.sendUpdate.bind(this))
this.ethStore.subscribe(this.sendUpdate.bind(this)) this.ethStore.subscribe(this.sendUpdate.bind(this))
this.txManager.memStore.subscribe(this.sendUpdate.bind(this)) this.txController.memStore.subscribe(this.sendUpdate.bind(this))
this.messageManager.memStore.subscribe(this.sendUpdate.bind(this)) this.messageManager.memStore.subscribe(this.sendUpdate.bind(this))
this.personalMessageManager.memStore.subscribe(this.sendUpdate.bind(this)) this.personalMessageManager.memStore.subscribe(this.sendUpdate.bind(this))
this.keyringController.memStore.subscribe(this.sendUpdate.bind(this)) this.keyringController.memStore.subscribe(this.sendUpdate.bind(this))
@ -251,7 +251,7 @@ module.exports = class MetamaskController extends EventEmitter {
}, },
this.networkController.getState(), this.networkController.getState(),
this.ethStore.getState(), this.ethStore.getState(),
this.txManager.memStore.getState(), this.txController.memStore.getState(),
this.messageManager.memStore.getState(), this.messageManager.memStore.getState(),
this.personalMessageManager.memStore.getState(), this.personalMessageManager.memStore.getState(),
this.keyringController.memStore.getState(), this.keyringController.memStore.getState(),
@ -276,7 +276,7 @@ module.exports = class MetamaskController extends EventEmitter {
getApi () { getApi () {
const keyringController = this.keyringController const keyringController = this.keyringController
const preferencesController = this.preferencesController const preferencesController = this.preferencesController
const txManager = this.txManager const txController = this.txController
const noticeController = this.noticeController const noticeController = this.noticeController
const addressBookController = this.addressBookController const addressBookController = this.addressBookController
@ -317,9 +317,9 @@ module.exports = class MetamaskController extends EventEmitter {
saveAccountLabel: nodeify(keyringController.saveAccountLabel).bind(keyringController), saveAccountLabel: nodeify(keyringController.saveAccountLabel).bind(keyringController),
exportAccount: nodeify(keyringController.exportAccount).bind(keyringController), exportAccount: nodeify(keyringController.exportAccount).bind(keyringController),
// txManager // txController
approveTransaction: txManager.approveTransaction.bind(txManager), approveTransaction: txController.approveTransaction.bind(txController),
cancelTransaction: txManager.cancelTransaction.bind(txManager), cancelTransaction: txController.cancelTransaction.bind(txController),
updateAndApproveTransaction: this.updateAndApproveTx.bind(this), updateAndApproveTransaction: this.updateAndApproveTx.bind(this),
// messageManager // messageManager
@ -449,12 +449,12 @@ module.exports = class MetamaskController extends EventEmitter {
newUnapprovedTransaction (txParams, cb) { newUnapprovedTransaction (txParams, cb) {
log.debug(`MetaMaskController newUnapprovedTransaction ${JSON.stringify(txParams)}`) log.debug(`MetaMaskController newUnapprovedTransaction ${JSON.stringify(txParams)}`)
const self = this const self = this
self.txManager.addUnapprovedTransaction(txParams, (err, txMeta) => { self.txController.addUnapprovedTransaction(txParams, (err, txMeta) => {
if (err) return cb(err) if (err) return cb(err)
self.sendUpdate() self.sendUpdate()
self.opts.showUnapprovedTx(txMeta) self.opts.showUnapprovedTx(txMeta)
// listen for tx completion (success, fail) // listen for tx completion (success, fail)
self.txManager.once(`${txMeta.id}:finished`, (completedTx) => { self.txController.once(`${txMeta.id}:finished`, (completedTx) => {
switch (completedTx.status) { switch (completedTx.status) {
case 'submitted': case 'submitted':
return cb(null, completedTx.hash) return cb(null, completedTx.hash)
@ -505,9 +505,9 @@ module.exports = class MetamaskController extends EventEmitter {
updateAndApproveTx (txMeta, cb) { updateAndApproveTx (txMeta, cb) {
log.debug(`MetaMaskController - updateAndApproveTx: ${JSON.stringify(txMeta)}`) log.debug(`MetaMaskController - updateAndApproveTx: ${JSON.stringify(txMeta)}`)
const txManager = this.txManager const txController = this.txController
txManager.updateTx(txMeta) txController.updateTx(txMeta)
txManager.approveTransaction(txMeta.id, cb) txController.approveTransaction(txMeta.id, cb)
} }
signMessage (msgParams, cb) { signMessage (msgParams, cb) {

View File

@ -0,0 +1,34 @@
const version = 13
/*
This migration modifies the network config from ambiguous 'testnet' to explicit 'ropsten'
*/
const clone = require('clone')
module.exports = {
version,
migrate: function (originalVersionedData) {
const versionedData = clone(originalVersionedData)
versionedData.meta.version = version
try {
const state = versionedData.data
const newState = transformState(state)
versionedData.data = newState
} catch (err) {
console.warn(`MetaMask Migration #${version}` + err.stack)
}
return Promise.resolve(versionedData)
},
}
function transformState (state) {
const newState = state
if (newState.config.provider.type === 'testnet') {
newState.config.provider.type = 'ropsten'
}
return newState
}

View File

@ -23,4 +23,5 @@ module.exports = [
require('./010'), require('./010'),
require('./011'), require('./011'),
require('./012'), require('./012'),
require('./013'),
] ]

View File

@ -1,6 +1,6 @@
machine: machine:
node: node:
version: 6.0.0 version: 7.6.0
dependencies: dependencies:
pre: pre:
- "npm i -g testem" - "npm i -g testem"

View File

@ -3,7 +3,7 @@ start the dual servers (dapp + mascara)
npm run mascara npm run mascara
``` ```
## First time use: ### First time use:
- navigate to: http://localhost:9001 - navigate to: http://localhost:9001
- Create an Account - Create an Account
@ -11,7 +11,7 @@ npm run mascara
- open devTools - open devTools
- click Sync Tx - click Sync Tx
## Tests: ### Tests:
``` ```
npm run testMascara npm run testMascara
@ -22,3 +22,12 @@ Test will run in browser, you will have to have these browsers installed:
- Chrome - Chrome
- Firefox - Firefox
- Opera - Opera
### Deploy:
Will build and deploy mascara via docker
```
docker-compose build && docker-compose stop && docker-compose up -d && docker-compose logs --tail 200 -f
```

View File

@ -2,7 +2,8 @@
<html> <html>
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<title>MetaMask Plugin</title> <title>MetaMascara Alpha</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head> </head>
<body> <body>
<div id="app-content"></div> <div id="app-content"></div>

View File

@ -34,7 +34,8 @@
"babelify", "babelify",
{ {
"presets": [ "presets": [
"es2015" "es2015",
"stage-0"
] ]
} }
], ],
@ -44,7 +45,7 @@
}, },
"dependencies": { "dependencies": {
"async": "^1.5.2", "async": "^1.5.2",
"async-q": "^0.3.1", "babel-runtime": "^6.23.0",
"bip39": "^2.2.0", "bip39": "^2.2.0",
"bluebird": "^3.5.0", "bluebird": "^3.5.0",
"browser-passworder": "^2.0.3", "browser-passworder": "^2.0.3",
@ -55,6 +56,7 @@
"debounce": "^1.0.0", "debounce": "^1.0.0",
"deep-extend": "^0.4.1", "deep-extend": "^0.4.1",
"denodeify": "^1.2.1", "denodeify": "^1.2.1",
"detect-node": "^2.0.3",
"disc": "^1.3.2", "disc": "^1.3.2",
"dnode": "^1.2.2", "dnode": "^1.2.2",
"end-of-stream": "^1.1.0", "end-of-stream": "^1.1.0",
@ -119,12 +121,14 @@
"valid-url": "^1.0.9", "valid-url": "^1.0.9",
"vreme": "^3.0.2", "vreme": "^3.0.2",
"web3": "0.18.2", "web3": "0.18.2",
"web3-provider-engine": "^12.0.3", "web3-provider-engine": "^12.0.6",
"web3-stream-provider": "^2.0.6", "web3-stream-provider": "^2.0.6",
"xtend": "^4.0.1" "xtend": "^4.0.1"
}, },
"devDependencies": { "devDependencies": {
"babel-eslint": "^6.0.5", "babel-eslint": "^6.0.5",
"babel-plugin-transform-runtime": "^6.23.0",
"babel-preset-stage-0": "^6.24.1",
"babel-register": "^6.7.2", "babel-register": "^6.7.2",
"babelify": "^7.2.0", "babelify": "^7.2.0",
"beefy": "^2.1.5", "beefy": "^2.1.5",
@ -135,6 +139,7 @@
"deep-freeze-strict": "^1.1.1", "deep-freeze-strict": "^1.1.1",
"del": "^2.2.0", "del": "^2.2.0",
"envify": "^4.0.0", "envify": "^4.0.0",
"enzyme": "^2.8.2",
"eslint-plugin-chai": "0.0.1", "eslint-plugin-chai": "0.0.1",
"eslint-plugin-mocha": "^4.9.0", "eslint-plugin-mocha": "^4.9.0",
"fs-promise": "^1.0.0", "fs-promise": "^1.0.0",
@ -161,6 +166,10 @@
"prompt": "^1.0.0", "prompt": "^1.0.0",
"qs": "^6.2.0", "qs": "^6.2.0",
"qunit": "^0.9.1", "qunit": "^0.9.1",
"react-addons-test-utils": "^15.5.1",
"react-dom": "^15.5.4",
"react-test-renderer": "^15.5.4",
"react-testutils-additions": "^15.2.0",
"sinon": "^1.17.3", "sinon": "^1.17.3",
"tape": "^4.5.1", "tape": "^4.5.1",
"testem": "^1.10.3", "testem": "^1.10.3",

View File

@ -1 +1,14 @@
{"version":0,"data":{"wallet":"{\"encSeed\":{\"encStr\":\"rT1C1jjkFRfmrwefscFcwZohl4f+HfIFlBZ9AM4ZD8atJmfKDIQCVK11NYDKYv8ZMIY03f3t8MuoZvfzBL8IJsWnZUhpzVTNNiARQJD2WpGA19eNBzgZm4vd0GwkIUruUDeJXu0iv2j9wU8hOQUqPbOePPy2Am5ro97iuvMAroRTnEKD60qFVg==\",\"nonce\":\"YUY2mwNq2v3FV0Fi94QnSiKFOLYfDR95\"},\"ksData\":{\"m/44'/60'/0'/0\":{\"info\":{\"curve\":\"secp256k1\",\"purpose\":\"sign\"},\"encHdPathPriv\":{\"encStr\":\"Iyi7ft4JQ9UtwrSXRT6ZIHPtZqJhe99rh0uWhNc6QLan6GanY2ZQeU0tt76CBealEWJyrJReSxGQdqDmSDYjpjH3m4JO5l0DfPLPseCqzXV/W+dzM0ubJ8lztLwpwi0L+vULNMqCx4dQtoNbNBq1QZUnjtpm6O8mWpScspboww==\",\"nonce\":\"Z7RqtjNjC6FrLUj5wVW1+HkjOW6Hib6K\"},\"hdIndex\":3,\"encPrivKeys\":{\"edb81c10122f34040cc4bef719a272fbbb1cf897\":{\"key\":\"8ab81tKBd4+CLAbzvS7SBFRTd6VWXBs86uBE43lgcmBu2U7UB22xdH64Q2hUf9eB\",\"nonce\":\"aGUEqI033FY39zKjWmZSI6PQrCLvkiRP\"},\"8bd7d5c000cf05284e98356370dc5ccaa3dbfc38\":{\"key\":\"+i3wmf4b+B898QtlOBfL0Ixirjg59/LLPX61vQ2L0xRPjXzNog0O4Wn15RemM5mY\",\"nonce\":\"imKrlkuoC5uuFkzJBbuDBluGCPJXNTKm\"},\"2340695474656e3124b8eba1172fbfb00eeac8f8\":{\"key\":\"pi+H9D8LYKsdCQKrfaJtsGFjE+X9s74xN675tsoIKrbPXhtpxMLOIQVtSqYveF62\",\"nonce\":\"49g80wDTovHwbguVVYf2FsYbp7Db5OAR\"}},\"addresses\":[\"edb81c10122f34040cc4bef719a272fbbb1cf897\",\"8bd7d5c000cf05284e98356370dc5ccaa3dbfc38\",\"2340695474656e3124b8eba1172fbfb00eeac8f8\"]}},\"version\":2}","config":{"provider":{"type":"etherscan"}}},"meta":{"version":0}} {
"version": 0,
"data": {
"wallet": "{\"encSeed\":{\"encStr\":\"rT1C1jjkFRfmrwefscFcwZohl4f+HfIFlBZ9AM4ZD8atJmfKDIQCVK11NYDKYv8ZMIY03f3t8MuoZvfzBL8IJsWnZUhpzVTNNiARQJD2WpGA19eNBzgZm4vd0GwkIUruUDeJXu0iv2j9wU8hOQUqPbOePPy2Am5ro97iuvMAroRTnEKD60qFVg==\",\"nonce\":\"YUY2mwNq2v3FV0Fi94QnSiKFOLYfDR95\"},\"ksData\":{\"m/44'/60'/0'/0\":{\"info\":{\"curve\":\"secp256k1\",\"purpose\":\"sign\"},\"encHdPathPriv\":{\"encStr\":\"Iyi7ft4JQ9UtwrSXRT6ZIHPtZqJhe99rh0uWhNc6QLan6GanY2ZQeU0tt76CBealEWJyrJReSxGQdqDmSDYjpjH3m4JO5l0DfPLPseCqzXV/W+dzM0ubJ8lztLwpwi0L+vULNMqCx4dQtoNbNBq1QZUnjtpm6O8mWpScspboww==\",\"nonce\":\"Z7RqtjNjC6FrLUj5wVW1+HkjOW6Hib6K\"},\"hdIndex\":3,\"encPrivKeys\":{\"edb81c10122f34040cc4bef719a272fbbb1cf897\":{\"key\":\"8ab81tKBd4+CLAbzvS7SBFRTd6VWXBs86uBE43lgcmBu2U7UB22xdH64Q2hUf9eB\",\"nonce\":\"aGUEqI033FY39zKjWmZSI6PQrCLvkiRP\"},\"8bd7d5c000cf05284e98356370dc5ccaa3dbfc38\":{\"key\":\"+i3wmf4b+B898QtlOBfL0Ixirjg59/LLPX61vQ2L0xRPjXzNog0O4Wn15RemM5mY\",\"nonce\":\"imKrlkuoC5uuFkzJBbuDBluGCPJXNTKm\"},\"2340695474656e3124b8eba1172fbfb00eeac8f8\":{\"key\":\"pi+H9D8LYKsdCQKrfaJtsGFjE+X9s74xN675tsoIKrbPXhtpxMLOIQVtSqYveF62\",\"nonce\":\"49g80wDTovHwbguVVYf2FsYbp7Db5OAR\"}},\"addresses\":[\"edb81c10122f34040cc4bef719a272fbbb1cf897\",\"8bd7d5c000cf05284e98356370dc5ccaa3dbfc38\",\"2340695474656e3124b8eba1172fbfb00eeac8f8\"]}},\"version\":2}",
"config": {
"provider": {
"type": "etherscan"
}
}
},
"meta": {
"version": 0
}
}

18
test/lib/mock-store.js Normal file
View File

@ -0,0 +1,18 @@
const createStore = require('redux').createStore
const applyMiddleware = require('redux').applyMiddleware
const thunkMiddleware = require('redux-thunk')
const createLogger = require('redux-logger')
const rootReducer = function() {}
module.exports = configureStore
const loggerMiddleware = createLogger()
const createStoreWithMiddleware = applyMiddleware(
thunkMiddleware,
loggerMiddleware
)(createStore)
function configureStore (initialState) {
return createStoreWithMiddleware(rootReducer, initialState)
}

View File

@ -0,0 +1,81 @@
const assert = require('assert')
const additions = require('react-testutils-additions')
const h = require('react-hyperscript')
const PendingTx = require('../../../ui/app/components/pending-tx')
const ReactTestUtils = require('react-addons-test-utils')
const ethUtil = require('ethereumjs-util')
describe('PendingTx', function () {
const identities = {
'0xfdea65c8e26263f6d9a1b5de9555d2931a33b826': {
name: 'Main Account 1',
balance: '0x00000000000000056bc75e2d63100000',
},
}
const gasPrice = '0x4A817C800' // 20 Gwei
const txData = {
'id':5021615666270214,
'time':1494458763011,
'status':'unapproved',
'metamaskNetworkId':'1494442339676',
'txParams':{
'from':'0xfdea65c8e26263f6d9a1b5de9555d2931a33b826',
'to':'0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb',
'value':'0xde0b6b3a7640000',
gasPrice,
'gas':'0x7b0c'},
'gasLimitSpecified':false,
'estimatedGas':'0x5208',
}
it('should use updated values when edited.', function (done) {
const renderer = ReactTestUtils.createRenderer()
const newGasPrice = '0x77359400'
const props = {
identities,
accounts: identities,
txData,
sendTransaction: (txMeta, event) => {
// Assert changes:
const result = ethUtil.addHexPrefix(txMeta.txParams.gasPrice)
assert.notEqual(result, gasPrice, 'gas price should change')
assert.equal(result, newGasPrice, 'gas price assigned.')
done()
},
}
const pendingTxComponent = h(PendingTx, props)
const component = additions.renderIntoDocument(pendingTxComponent)
renderer.render(pendingTxComponent)
const result = renderer.getRenderOutput()
assert.equal(result.type, 'div', 'should create a div')
try {
const input = additions.find(component, '.cell.row input[type="number"]')[1]
ReactTestUtils.Simulate.change(input, {
target: {
value: 2,
checkValidity() { return true },
},
})
const form = additions.find(component, 'form')[0]
form.checkValidity = () => true
form.getFormEl = () => { return { checkValidity() { return true } } }
ReactTestUtils.Simulate.submit(form, { preventDefault() {}, target: { checkValidity() {
return true
} } })
} catch (e) {
console.log('WHAAAA')
console.error(e)
}
})
})

View File

@ -16,6 +16,7 @@ const migration9 = require(path.join('..', '..', 'app', 'scripts', 'migrations',
const migration10 = require(path.join('..', '..', 'app', 'scripts', 'migrations', '010')) const migration10 = require(path.join('..', '..', 'app', 'scripts', 'migrations', '010'))
const migration11 = require(path.join('..', '..', 'app', 'scripts', 'migrations', '011')) const migration11 = require(path.join('..', '..', 'app', 'scripts', 'migrations', '011'))
const migration12 = require(path.join('..', '..', 'app', 'scripts', 'migrations', '012')) const migration12 = require(path.join('..', '..', 'app', 'scripts', 'migrations', '012'))
const migration13 = require(path.join('..', '..', 'app', 'scripts', 'migrations', '013'))
const oldTestRpc = 'https://rawtestrpc.metamask.io/' const oldTestRpc = 'https://rawtestrpc.metamask.io/'
@ -97,6 +98,11 @@ describe('wallet1 is migrated successfully', () => {
}).then((twelfthResult) => { }).then((twelfthResult) => {
assert.equal(twelfthResult.data.NoticeController.noticesList[0].body, '', 'notices that have been read should have an empty body.') assert.equal(twelfthResult.data.NoticeController.noticesList[0].body, '', 'notices that have been read should have an empty body.')
assert.equal(twelfthResult.data.NoticeController.noticesList[1].body, 'nonempty', 'notices that have not been read should not have an empty body.') assert.equal(twelfthResult.data.NoticeController.noticesList[1].body, 'nonempty', 'notices that have not been read should not have an empty body.')
assert.equal(twelfthResult.data.config.provider.type, 'testnet', 'network is originally testnet.')
return migration13.migrate(twelfthResult)
}).then((thirteenthResult) => {
assert.equal(thirteenthResult.data.config.provider.type, 'ropsten', 'network has been changed to ropsten.')
}) })
}) })
}) })

View File

@ -0,0 +1,41 @@
const assert = require('assert')
const clone = require('clone')
const Migrator = require('../../app/scripts/lib/migrator/')
const migrations = [
{
version: 1,
migrate: (data) => {
// clone the data just like we do in migrations
const clonedData = clone(data)
clonedData.meta.version = 1
return Promise.resolve(clonedData)
},
},
{
version: 2,
migrate: (data) => {
const clonedData = clone(data)
clonedData.meta.version = 2
return Promise.resolve(clonedData)
},
},
{
version: 3,
migrate: (data) => {
const clonedData = clone(data)
clonedData.meta.version = 3
return Promise.resolve(clonedData)
},
},
]
const versionedData = {meta: {version: 0}, data:{hello:'world'}}
describe('Migrator', () => {
const migrator = new Migrator({ migrations })
it('migratedData version should be version 3', (done) => {
migrator.migrateData(versionedData)
.then((migratedData) => {
assert.equal(migratedData.meta.version, migrations[2].version)
done()
}).catch(done)
})
})

View File

@ -3,17 +3,19 @@ const EventEmitter = require('events')
const ethUtil = require('ethereumjs-util') const ethUtil = require('ethereumjs-util')
const EthTx = require('ethereumjs-tx') const EthTx = require('ethereumjs-tx')
const ObservableStore = require('obs-store') const ObservableStore = require('obs-store')
const TransactionManager = require('../../app/scripts/transaction-manager') const clone = require('clone')
const sinon = require('sinon')
const TransactionController = require('../../app/scripts/controllers/transactions')
const noop = () => true const noop = () => true
const currentNetworkId = 42 const currentNetworkId = 42
const otherNetworkId = 36 const otherNetworkId = 36
const privKey = new Buffer('8718b9618a37d1fc78c436511fc6df3c8258d3250635bba617f33003270ec03e', 'hex') const privKey = new Buffer('8718b9618a37d1fc78c436511fc6df3c8258d3250635bba617f33003270ec03e', 'hex')
describe('Transaction Manager', function () { describe('Transaction Controller', function () {
let txManager let txController
beforeEach(function () { beforeEach(function () {
txManager = new TransactionManager({ txController = new TransactionController({
networkStore: new ObservableStore({ network: currentNetworkId }), networkStore: new ObservableStore({ network: currentNetworkId }),
txHistoryLimit: 10, txHistoryLimit: 10,
blockTracker: new EventEmitter(), blockTracker: new EventEmitter(),
@ -29,7 +31,7 @@ describe('Transaction Manager', function () {
var sample = { var sample = {
value: '0x01', value: '0x01',
} }
txManager.txProviderUtils.validateTxParams(sample, (err) => { txController.txProviderUtils.validateTxParams(sample, (err) => {
assert.equal(err, null, 'no error') assert.equal(err, null, 'no error')
}) })
}) })
@ -38,7 +40,7 @@ describe('Transaction Manager', function () {
var sample = { var sample = {
value: '-0x01', value: '-0x01',
} }
txManager.txProviderUtils.validateTxParams(sample, (err) => { txController.txProviderUtils.validateTxParams(sample, (err) => {
assert.ok(err, 'error') assert.ok(err, 'error')
}) })
}) })
@ -46,7 +48,7 @@ describe('Transaction Manager', function () {
describe('#getTxList', function () { describe('#getTxList', function () {
it('when new should return empty array', function () { it('when new should return empty array', function () {
var result = txManager.getTxList() var result = txController.getTxList()
assert.ok(Array.isArray(result)) assert.ok(Array.isArray(result))
assert.equal(result.length, 0) assert.equal(result.length, 0)
}) })
@ -58,8 +60,8 @@ 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', metamaskNetworkId: currentNetworkId, txParams: {} } var tx = { id: 1, status: 'confirmed', metamaskNetworkId: currentNetworkId, txParams: {} }
txManager.addTx(tx, noop) txController.addTx(tx, noop)
var result = txManager.getTxList() var result = txController.getTxList()
assert.ok(Array.isArray(result)) assert.ok(Array.isArray(result))
assert.equal(result.length, 1) assert.equal(result.length, 1)
assert.equal(result[0].id, 1) assert.equal(result[0].id, 1)
@ -68,45 +70,45 @@ describe('Transaction Manager', function () {
it('does not override txs from other networks', function () { it('does not override txs from other networks', function () {
var tx = { id: 1, status: 'confirmed', metamaskNetworkId: currentNetworkId, txParams: {} } var tx = { id: 1, status: 'confirmed', metamaskNetworkId: currentNetworkId, txParams: {} }
var tx2 = { id: 2, status: 'confirmed', metamaskNetworkId: otherNetworkId, txParams: {} } var tx2 = { id: 2, status: 'confirmed', metamaskNetworkId: otherNetworkId, txParams: {} }
txManager.addTx(tx, noop) txController.addTx(tx, noop)
txManager.addTx(tx2, noop) txController.addTx(tx2, noop)
var result = txManager.getFullTxList() var result = txController.getFullTxList()
var result2 = txManager.getTxList() var result2 = txController.getTxList()
assert.equal(result.length, 2, 'txs were deleted') assert.equal(result.length, 2, 'txs were deleted')
assert.equal(result2.length, 1, 'incorrect number of txs on network.') assert.equal(result2.length, 1, 'incorrect number of txs on network.')
}) })
it('cuts off early txs beyond a limit', function () { it('cuts off early txs beyond a limit', function () {
const limit = txManager.txHistoryLimit const limit = txController.txHistoryLimit
for (let i = 0; i < limit + 1; i++) { for (let i = 0; i < limit + 1; i++) {
const tx = { id: i, time: new Date(), status: 'confirmed', metamaskNetworkId: currentNetworkId, txParams: {} } const tx = { id: i, time: new Date(), status: 'confirmed', metamaskNetworkId: currentNetworkId, txParams: {} }
txManager.addTx(tx, noop) txController.addTx(tx, noop)
} }
var result = txManager.getTxList() var result = txController.getTxList()
assert.equal(result.length, limit, `limit of ${limit} txs enforced`) assert.equal(result.length, limit, `limit of ${limit} txs enforced`)
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 whether 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 = txController.txHistoryLimit
for (let i = 0; i < limit + 1; i++) { for (let i = 0; i < limit + 1; i++) {
const tx = { id: i, time: new Date(), status: 'rejected', metamaskNetworkId: currentNetworkId, txParams: {} } const tx = { id: i, time: new Date(), status: 'rejected', metamaskNetworkId: currentNetworkId, txParams: {} }
txManager.addTx(tx, noop) txController.addTx(tx, noop)
} }
var result = txManager.getTxList() var result = txController.getTxList()
assert.equal(result.length, limit, `limit of ${limit} txs enforced`) assert.equal(result.length, limit, `limit of ${limit} txs enforced`)
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 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', metamaskNetworkId: currentNetworkId, txParams: {} } var unconfirmedTx = { id: 0, time: new Date(), status: 'unapproved', metamaskNetworkId: currentNetworkId, txParams: {} }
txManager.addTx(unconfirmedTx, noop) txController.addTx(unconfirmedTx, noop)
const limit = txManager.txHistoryLimit const limit = txController.txHistoryLimit
for (let i = 1; i < limit + 1; i++) { for (let i = 1; i < limit + 1; i++) {
const tx = { id: i, time: new Date(), status: 'confirmed', metamaskNetworkId: currentNetworkId, txParams: {} } const tx = { id: i, time: new Date(), status: 'confirmed', metamaskNetworkId: currentNetworkId, txParams: {} }
txManager.addTx(tx, noop) txController.addTx(tx, noop)
} }
var result = txManager.getTxList() var result = txController.getTxList()
assert.equal(result.length, limit, `limit of ${limit} txs enforced`) assert.equal(result.length, limit, `limit of ${limit} txs enforced`)
assert.equal(result[0].id, 0, 'first tx should still be there') assert.equal(result[0].id, 0, 'first tx should still be there')
assert.equal(result[0].status, 'unapproved', 'first tx should be unapproved') assert.equal(result[0].status, 'unapproved', 'first tx should be unapproved')
@ -117,9 +119,9 @@ 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', metamaskNetworkId: currentNetworkId, txParams: {} } var tx = { id: 1, status: 'unapproved', metamaskNetworkId: currentNetworkId, txParams: {} }
txManager.addTx(tx, noop) txController.addTx(tx, noop)
txManager.setTxStatusSigned(1) txController.setTxStatusSigned(1)
var result = txManager.getTxList() var result = txController.getTxList()
assert.ok(Array.isArray(result)) assert.ok(Array.isArray(result))
assert.equal(result.length, 1) assert.equal(result.length, 1)
assert.equal(result[0].status, 'signed') assert.equal(result[0].status, 'signed')
@ -132,18 +134,18 @@ describe('Transaction Manager', function () {
assert(true, 'event listener has been triggered and noop executed') assert(true, 'event listener has been triggered and noop executed')
done() done()
} }
txManager.addTx(tx) txController.addTx(tx)
txManager.on('1:signed', noop) txController.on('1:signed', noop)
txManager.setTxStatusSigned(1) txController.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', metamaskNetworkId: currentNetworkId, txParams: {} } var tx = { id: 1, status: 'unapproved', metamaskNetworkId: currentNetworkId, txParams: {} }
txManager.addTx(tx) txController.addTx(tx)
txManager.setTxStatusRejected(1) txController.setTxStatusRejected(1)
var result = txManager.getTxList() var result = txController.getTxList()
assert.ok(Array.isArray(result)) assert.ok(Array.isArray(result))
assert.equal(result.length, 1) assert.equal(result.length, 1)
assert.equal(result[0].status, 'rejected') assert.equal(result[0].status, 'rejected')
@ -152,31 +154,53 @@ 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', metamaskNetworkId: currentNetworkId, txParams: {} } var tx = { id: 1, status: 'unapproved', metamaskNetworkId: currentNetworkId, txParams: {} }
txManager.addTx(tx) txController.addTx(tx)
const noop = function (err, txId) { const noop = function (err, txId) {
assert(true, 'event listener has been triggered and noop executed') assert(true, 'event listener has been triggered and noop executed')
done() done()
} }
txManager.on('1:rejected', noop) txController.on('1:rejected', noop)
txManager.setTxStatusRejected(1) txController.setTxStatusRejected(1)
}) })
}) })
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', metamaskNetworkId: currentNetworkId, txParams: {} }, noop) txController.addTx({ id: '1', status: 'unapproved', metamaskNetworkId: currentNetworkId, txParams: {} }, noop)
txManager.addTx({ id: '2', status: 'confirmed', metamaskNetworkId: currentNetworkId, txParams: {} }, noop) txController.addTx({ id: '2', status: 'confirmed', metamaskNetworkId: currentNetworkId, txParams: {} }, noop)
txManager.updateTx({ id: '1', status: 'blah', hash: 'foo', metamaskNetworkId: currentNetworkId, txParams: {} }) txController.updateTx({ id: '1', status: 'blah', hash: 'foo', metamaskNetworkId: currentNetworkId, txParams: {} })
var result = txManager.getTx('1') var result = txController.getTx('1')
assert.equal(result.hash, 'foo') assert.equal(result.hash, 'foo')
}) })
it('updates gas price', function () {
const originalGasPrice = '0x01'
const desiredGasPrice = '0x02'
const txMeta = {
id: '1',
status: 'unapproved',
metamaskNetworkId: currentNetworkId,
txParams: {
gasPrice: originalGasPrice,
},
}
const updatedMeta = clone(txMeta)
txController.addTx(txMeta)
updatedMeta.txParams.gasPrice = desiredGasPrice
txController.updateTx(updatedMeta)
var result = txController.getTx('1')
assert.equal(result.txParams.gasPrice, desiredGasPrice, 'gas price updated')
})
}) })
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', metamaskNetworkId: currentNetworkId, txParams: {} }, noop) txController.addTx({ id: '1', status: 'unapproved', metamaskNetworkId: currentNetworkId, txParams: {} }, noop)
txManager.addTx({ id: '2', status: 'confirmed', metamaskNetworkId: currentNetworkId, txParams: {} }, noop) txController.addTx({ id: '2', status: 'confirmed', metamaskNetworkId: currentNetworkId, txParams: {} }, noop)
const result = txManager.getUnapprovedTxList() const result = txController.getUnapprovedTxList()
assert.equal(typeof result, 'object') assert.equal(typeof result, 'object')
assert.equal(result['1'].status, 'unapproved') assert.equal(result['1'].status, 'unapproved')
assert.equal(result['2'], undefined) assert.equal(result['2'], undefined)
@ -185,10 +209,10 @@ 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', metamaskNetworkId: currentNetworkId, txParams: {} }, noop) txController.addTx({ id: '1', status: 'unapproved', metamaskNetworkId: currentNetworkId, txParams: {} }, noop)
txManager.addTx({ id: '2', status: 'confirmed', metamaskNetworkId: currentNetworkId, txParams: {} }, noop) txController.addTx({ id: '2', status: 'confirmed', metamaskNetworkId: currentNetworkId, txParams: {} }, noop)
assert.equal(txManager.getTx('1').status, 'unapproved') assert.equal(txController.getTx('1').status, 'unapproved')
assert.equal(txManager.getTx('2').status, 'confirmed') assert.equal(txController.getTx('2').status, 'confirmed')
}) })
}) })
@ -206,32 +230,93 @@ describe('Transaction Manager', function () {
{ id: 8, status: 'confirmed', txParams: { from: '0xbb', to: '0xaa' }, metamaskNetworkId: currentNetworkId }, { id: 8, status: 'confirmed', txParams: { from: '0xbb', to: '0xaa' }, metamaskNetworkId: currentNetworkId },
{ id: 9, status: 'confirmed', txParams: { from: '0xbb', to: '0xaa' }, metamaskNetworkId: currentNetworkId }, { id: 9, status: 'confirmed', txParams: { from: '0xbb', to: '0xaa' }, metamaskNetworkId: currentNetworkId },
] ]
txMetas.forEach((txMeta) => txManager.addTx(txMeta, noop)) txMetas.forEach((txMeta) => txController.addTx(txMeta, noop))
let filterParams let filterParams
filterParams = { status: 'unapproved', from: '0xaa' } filterParams = { status: 'unapproved', from: '0xaa' }
assert.equal(txManager.getFilteredTxList(filterParams).length, 3, `getFilteredTxList - ${JSON.stringify(filterParams)}`) assert.equal(txController.getFilteredTxList(filterParams).length, 3, `getFilteredTxList - ${JSON.stringify(filterParams)}`)
filterParams = { status: 'unapproved', to: '0xaa' } filterParams = { status: 'unapproved', to: '0xaa' }
assert.equal(txManager.getFilteredTxList(filterParams).length, 2, `getFilteredTxList - ${JSON.stringify(filterParams)}`) assert.equal(txController.getFilteredTxList(filterParams).length, 2, `getFilteredTxList - ${JSON.stringify(filterParams)}`)
filterParams = { status: 'confirmed', from: '0xbb' } filterParams = { status: 'confirmed', from: '0xbb' }
assert.equal(txManager.getFilteredTxList(filterParams).length, 3, `getFilteredTxList - ${JSON.stringify(filterParams)}`) assert.equal(txController.getFilteredTxList(filterParams).length, 3, `getFilteredTxList - ${JSON.stringify(filterParams)}`)
filterParams = { status: 'confirmed' } filterParams = { status: 'confirmed' }
assert.equal(txManager.getFilteredTxList(filterParams).length, 5, `getFilteredTxList - ${JSON.stringify(filterParams)}`) assert.equal(txController.getFilteredTxList(filterParams).length, 5, `getFilteredTxList - ${JSON.stringify(filterParams)}`)
filterParams = { from: '0xaa' } filterParams = { from: '0xaa' }
assert.equal(txManager.getFilteredTxList(filterParams).length, 5, `getFilteredTxList - ${JSON.stringify(filterParams)}`) assert.equal(txController.getFilteredTxList(filterParams).length, 5, `getFilteredTxList - ${JSON.stringify(filterParams)}`)
filterParams = { to: '0xaa' } filterParams = { to: '0xaa' }
assert.equal(txManager.getFilteredTxList(filterParams).length, 5, `getFilteredTxList - ${JSON.stringify(filterParams)}`) assert.equal(txController.getFilteredTxList(filterParams).length, 5, `getFilteredTxList - ${JSON.stringify(filterParams)}`)
})
})
describe('#approveTransaction', function () {
let txMeta, originalValue
beforeEach(function () {
originalValue = '0x01'
txMeta = {
id: '1',
status: 'unapproved',
metamaskNetworkId: currentNetworkId,
txParams: {
nonce: originalValue,
gas: originalValue,
gasPrice: originalValue,
},
}
})
it('does not overwrite set values', function (done) {
const wrongValue = '0x05'
txController.addTx(txMeta)
const estimateStub = sinon.stub(txController.txProviderUtils.query, 'estimateGas')
.callsArgWith(1, null, wrongValue)
const priceStub = sinon.stub(txController.txProviderUtils.query, 'gasPrice')
.callsArgWith(0, null, wrongValue)
const nonceStub = sinon.stub(txController.txProviderUtils.query, 'getTransactionCount')
.callsArgWith(2, null, wrongValue)
const signStub = sinon.stub(txController, 'signTransaction')
.callsArgWith(1, null, noop)
const pubStub = sinon.stub(txController.txProviderUtils, 'publishTransaction')
.callsArgWith(1, null, originalValue)
txController.approveTransaction(txMeta.id, (err) => {
assert.ifError(err, 'should not error')
const result = txController.getTx(txMeta.id)
const params = result.txParams
assert.equal(params.gas, originalValue, 'gas unmodified')
assert.equal(params.gasPrice, originalValue, 'gas price unmodified')
assert.equal(params.nonce, originalValue, 'nonce unmodified')
assert.equal(result.hash, originalValue, 'hash was set')
estimateStub.restore()
priceStub.restore()
signStub.restore()
nonceStub.restore()
pubStub.restore()
done()
})
}) })
}) })
describe('#sign replay-protected tx', function () { describe('#sign replay-protected tx', function () {
it('prepares a tx with the chainId set', function () { it('prepares a tx with the chainId set', function () {
txManager.addTx({ id: '1', status: 'unapproved', metamaskNetworkId: currentNetworkId, txParams: {} }, noop) txController.addTx({ id: '1', status: 'unapproved', metamaskNetworkId: currentNetworkId, txParams: {} }, noop)
txManager.signTransaction('1', (err, rawTx) => { txController.signTransaction('1', (err, rawTx) => {
if (err) return assert.fail('it should not fail') if (err) return assert.fail('it should not fail')
const ethTx = new EthTx(ethUtil.toBuffer(rawTx)) const ethTx = new EthTx(ethUtil.toBuffer(rawTx))
assert.equal(ethTx.getChainId(), currentNetworkId) assert.equal(ethTx.getChainId(), currentNetworkId)
}) })
}) })
}) })
}) })

View File

@ -29,6 +29,8 @@ function mapStateToProps (state) {
unapprovedMsgs: valuesFor(state.metamask.unapprovedMsgs), unapprovedMsgs: valuesFor(state.metamask.unapprovedMsgs),
shapeShiftTxList: state.metamask.shapeShiftTxList, shapeShiftTxList: state.metamask.shapeShiftTxList,
transactions: state.metamask.selectedAddressTxList || [], transactions: state.metamask.selectedAddressTxList || [],
conversionRate: state.metamask.conversionRate,
currentCurrency: state.metamask.currentCurrency,
} }
} }
@ -43,7 +45,7 @@ AccountDetailScreen.prototype.render = function () {
var checksumAddress = selected && ethUtil.toChecksumAddress(selected) var checksumAddress = selected && ethUtil.toChecksumAddress(selected)
var identity = props.identities[selected] var identity = props.identities[selected]
var account = props.accounts[selected] var account = props.accounts[selected]
const { network } = props const { network, conversionRate, currentCurrency } = props
return ( return (
@ -182,6 +184,8 @@ AccountDetailScreen.prototype.render = function () {
h(EthBalance, { h(EthBalance, {
value: account && account.balance, value: account && account.balance,
conversionRate,
currentCurrency,
style: { style: {
lineHeight: '7px', lineHeight: '7px',
marginTop: '10px', marginTop: '10px',
@ -243,11 +247,13 @@ AccountDetailScreen.prototype.subview = function () {
} }
AccountDetailScreen.prototype.transactionList = function () { AccountDetailScreen.prototype.transactionList = function () {
const {transactions, unapprovedMsgs, address, network, shapeShiftTxList } = this.props const {transactions, unapprovedMsgs, address,
network, shapeShiftTxList, conversionRate } = this.props
return h(TransactionList, { return h(TransactionList, {
transactions: transactions.sort((a, b) => b.time - a.time), transactions: transactions.sort((a, b) => b.time - a.time),
network, network,
unapprovedMsgs, unapprovedMsgs,
conversionRate,
address, address,
shapeShiftTxList, shapeShiftTxList,
viewPendingTx: (txId) => { viewPendingTx: (txId) => {

View File

@ -15,7 +15,8 @@ function AccountListItem () {
} }
AccountListItem.prototype.render = function () { AccountListItem.prototype.render = function () {
const { identity, selectedAddress, accounts, onShowDetail } = this.props const { identity, selectedAddress, accounts, onShowDetail,
conversionRate, currentCurrency } = this.props
const checksumAddress = identity && identity.address && ethUtil.toChecksumAddress(identity.address) const checksumAddress = identity && identity.address && ethUtil.toChecksumAddress(identity.address)
const isSelected = selectedAddress === identity.address const isSelected = selectedAddress === identity.address
@ -52,6 +53,8 @@ AccountListItem.prototype.render = function () {
}, checksumAddress), }, checksumAddress),
h(EthBalance, { h(EthBalance, {
value: account && account.balance, value: account && account.balance,
currentCurrency,
conversionRate,
style: { style: {
lineHeight: '7px', lineHeight: '7px',
marginTop: '10px', marginTop: '10px',

View File

@ -23,6 +23,8 @@ function mapStateToProps (state) {
scrollToBottom: state.appState.scrollToBottom, scrollToBottom: state.appState.scrollToBottom,
pending, pending,
keyrings: state.metamask.keyrings, keyrings: state.metamask.keyrings,
conversionRate: state.metamask.conversionRate,
currentCurrency: state.metamask.currentCurrency,
} }
} }
@ -33,7 +35,7 @@ function AccountsScreen () {
AccountsScreen.prototype.render = function () { AccountsScreen.prototype.render = function () {
const props = this.props const props = this.props
const { keyrings } = props const { keyrings, conversionRate, currentCurrency } = props
const identityList = valuesFor(props.identities) const identityList = valuesFor(props.identities)
const unapprovedTxList = valuesFor(props.unapprovedTxs) const unapprovedTxList = valuesFor(props.unapprovedTxs)
@ -81,6 +83,8 @@ AccountsScreen.prototype.render = function () {
key: `acct-panel-${identity.address}`, key: `acct-panel-${identity.address}`,
identity, identity,
selectedAddress: this.props.selectedAddress, selectedAddress: this.props.selectedAddress,
conversionRate,
currentCurrency,
accounts: this.props.accounts, accounts: this.props.accounts,
onShowDetail: this.onShowDetail.bind(this), onShowDetail: this.onShowDetail.bind(this),
pending, pending,

View File

@ -249,7 +249,7 @@ App.prototype.renderNetworkDropdown = function () {
h(DropMenuItem, { h(DropMenuItem, {
label: 'Ropsten Test Network', label: 'Ropsten Test Network',
closeMenu: () => this.setState({ isNetworkMenuOpen: false }), closeMenu: () => this.setState({ isNetworkMenuOpen: false }),
action: () => props.dispatch(actions.setProviderType('testnet')), action: () => props.dispatch(actions.setProviderType('ropsten')),
icon: h('.menu-icon.red-dot'), icon: h('.menu-icon.red-dot'),
activeNetworkRender: props.network, activeNetworkRender: props.network,
provider: props.provider, provider: props.provider,

View File

@ -42,7 +42,7 @@ DropMenuItem.prototype.activeNetworkRender = function () {
if (providerType === 'mainnet') return h('.check', '✓') if (providerType === 'mainnet') return h('.check', '✓')
break break
case 'Ropsten Test Network': case 'Ropsten Test Network':
if (providerType === 'testnet') return h('.check', '✓') if (providerType === 'ropsten') return h('.check', '✓')
break break
case 'Kovan Test Network': case 'Kovan Test Network':
if (providerType === 'kovan') return h('.check', '✓') if (providerType === 'kovan') return h('.check', '✓')

View File

@ -5,11 +5,9 @@ const extend = require('xtend')
const debounce = require('debounce') const debounce = require('debounce')
const copyToClipboard = require('copy-to-clipboard') const copyToClipboard = require('copy-to-clipboard')
const ENS = require('ethjs-ens') const ENS = require('ethjs-ens')
const networkMap = require('ethjs-ens/lib/network-map.json')
const ensRE = /.+\.eth$/ const ensRE = /.+\.eth$/
const networkResolvers = {
'3': '112234455c3a32fd11230c42e7bccd4a84e02010',
}
module.exports = EnsInput module.exports = EnsInput
@ -24,8 +22,8 @@ EnsInput.prototype.render = function () {
list: 'addresses', list: 'addresses',
onChange: () => { onChange: () => {
const network = this.props.network const network = this.props.network
const resolverAddress = networkResolvers[network] const networkHasEnsSupport = getNetworkEnsSupport(network)
if (!resolverAddress) return if (!networkHasEnsSupport) return
const recipient = document.querySelector('input[name="address"]').value const recipient = document.querySelector('input[name="address"]').value
if (recipient.match(ensRE) === null) { if (recipient.match(ensRE) === null) {
@ -73,9 +71,9 @@ EnsInput.prototype.render = function () {
EnsInput.prototype.componentDidMount = function () { EnsInput.prototype.componentDidMount = function () {
const network = this.props.network const network = this.props.network
const resolverAddress = networkResolvers[network] const networkHasEnsSupport = getNetworkEnsSupport(network)
if (resolverAddress) { if (networkHasEnsSupport) {
const provider = global.ethereumProvider const provider = global.ethereumProvider
this.ens = new ENS({ provider, network }) this.ens = new ENS({ provider, network })
this.checkName = debounce(this.lookupEnsName.bind(this), 200) this.checkName = debounce(this.lookupEnsName.bind(this), 200)
@ -169,3 +167,8 @@ EnsInput.prototype.ensIconContents = function (recipient) {
}) })
} }
} }
function getNetworkEnsSupport (network) {
return Boolean(networkMap[network])
}

View File

@ -16,20 +16,19 @@ function EthBalanceComponent () {
EthBalanceComponent.prototype.render = function () { EthBalanceComponent.prototype.render = function () {
var props = this.props var props = this.props
let { value } = props let { value } = props
var style = props.style const { style, width } = props
var needsParse = this.props.needsParse !== undefined ? this.props.needsParse : true var needsParse = this.props.needsParse !== undefined ? this.props.needsParse : true
value = value ? formatBalance(value, 6, needsParse) : '...' value = value ? formatBalance(value, 6, needsParse) : '...'
var width = props.width
return ( return (
h('.ether-balance.ether-balance-amount', { h('.ether-balance.ether-balance-amount', {
style: style, style,
}, [ }, [
h('div', { h('div', {
style: { style: {
display: 'inline', display: 'inline',
width: width, width,
}, },
}, this.renderBalance(value)), }, this.renderBalance(value)),
]) ])
@ -38,16 +37,17 @@ EthBalanceComponent.prototype.render = function () {
} }
EthBalanceComponent.prototype.renderBalance = function (value) { EthBalanceComponent.prototype.renderBalance = function (value) {
var props = this.props var props = this.props
const { conversionRate, shorten, incoming, currentCurrency } = props
if (value === 'None') return value if (value === 'None') return value
if (value === '...') return value if (value === '...') return value
var balanceObj = generateBalanceObject(value, props.shorten ? 1 : 3) var balanceObj = generateBalanceObject(value, shorten ? 1 : 3)
var balance var balance
var splitBalance = value.split(' ') var splitBalance = value.split(' ')
var ethNumber = splitBalance[0] var ethNumber = splitBalance[0]
var ethSuffix = splitBalance[1] var ethSuffix = splitBalance[1]
const showFiat = 'showFiat' in props ? props.showFiat : true const showFiat = 'showFiat' in props ? props.showFiat : true
if (props.shorten) { if (shorten) {
balance = balanceObj.shortBalance balance = balanceObj.shortBalance
} else { } else {
balance = balanceObj.balance balance = balanceObj.balance
@ -73,7 +73,7 @@ EthBalanceComponent.prototype.renderBalance = function (value) {
width: '100%', width: '100%',
textAlign: 'right', textAlign: 'right',
}, },
}, this.props.incoming ? `+${balance}` : balance), }, incoming ? `+${balance}` : balance),
h('div', { h('div', {
style: { style: {
color: ' #AEAEAE', color: ' #AEAEAE',
@ -83,7 +83,7 @@ EthBalanceComponent.prototype.renderBalance = function (value) {
}, label), }, label),
]), ]),
showFiat ? h(FiatValue, { value: props.value }) : null, showFiat ? h(FiatValue, { value: props.value, conversionRate, currentCurrency }) : null,
])) ]))
) )
} }

View File

@ -1,17 +1,9 @@
const Component = require('react').Component const Component = require('react').Component
const h = require('react-hyperscript') const h = require('react-hyperscript')
const inherits = require('util').inherits const inherits = require('util').inherits
const connect = require('react-redux').connect
const formatBalance = require('../util').formatBalance const formatBalance = require('../util').formatBalance
module.exports = connect(mapStateToProps)(FiatValue) module.exports = FiatValue
function mapStateToProps (state) {
return {
conversionRate: state.metamask.conversionRate,
currentCurrency: state.metamask.currentCurrency,
}
}
inherits(FiatValue, Component) inherits(FiatValue, Component)
function FiatValue () { function FiatValue () {
@ -20,23 +12,23 @@ function FiatValue () {
FiatValue.prototype.render = function () { FiatValue.prototype.render = function () {
const props = this.props const props = this.props
const { conversionRate, currentCurrency } = props
const value = formatBalance(props.value, 6) const value = formatBalance(props.value, 6)
if (value === 'None') return value if (value === 'None') return value
var fiatDisplayNumber, fiatTooltipNumber var fiatDisplayNumber, fiatTooltipNumber
var splitBalance = value.split(' ') var splitBalance = value.split(' ')
if (props.conversionRate !== 0) { if (conversionRate !== 0) {
fiatTooltipNumber = Number(splitBalance[0]) * props.conversionRate fiatTooltipNumber = Number(splitBalance[0]) * conversionRate
fiatDisplayNumber = fiatTooltipNumber.toFixed(2) fiatDisplayNumber = fiatTooltipNumber.toFixed(2)
} else { } else {
fiatDisplayNumber = 'N/A' fiatDisplayNumber = 'N/A'
fiatTooltipNumber = 'Unknown' fiatTooltipNumber = 'Unknown'
} }
var fiatSuffix = props.currentCurrency return fiatDisplay(fiatDisplayNumber, currentCurrency)
return fiatDisplay(fiatDisplayNumber, fiatSuffix)
} }
function fiatDisplay (fiatDisplayNumber, fiatSuffix) { function fiatDisplay (fiatDisplayNumber, fiatSuffix) {

View File

@ -139,7 +139,7 @@ HexAsDecimalInput.prototype.constructWarning = function () {
} }
function hexify (decimalString) { function hexify (decimalString) {
const hexBN = new BN(decimalString, 10) const hexBN = new BN(parseInt(decimalString), 10)
return '0x' + hexBN.toString('hex') return '0x' + hexBN.toString('hex')
} }

View File

@ -1,6 +1,7 @@
const Component = require('react').Component const Component = require('react').Component
const h = require('react-hyperscript') const h = require('react-hyperscript')
const inherits = require('util').inherits const inherits = require('util').inherits
const isNode = require('detect-node')
const findDOMNode = require('react-dom').findDOMNode const findDOMNode = require('react-dom').findDOMNode
const jazzicon = require('jazzicon') const jazzicon = require('jazzicon')
const iconFactoryGen = require('../../lib/icon-factory') const iconFactoryGen = require('../../lib/icon-factory')
@ -40,8 +41,10 @@ IdenticonComponent.prototype.componentDidMount = function () {
var container = findDOMNode(this) var container = findDOMNode(this)
var diameter = props.diameter || this.defaultDiameter var diameter = props.diameter || this.defaultDiameter
var img = iconFactory.iconForAddress(address, diameter, false) if (!isNode) {
container.appendChild(img) var img = iconFactory.iconForAddress(address, diameter, false)
container.appendChild(img)
}
} }
IdenticonComponent.prototype.componentDidUpdate = function () { IdenticonComponent.prototype.componentDidUpdate = function () {
@ -58,6 +61,8 @@ IdenticonComponent.prototype.componentDidUpdate = function () {
} }
var diameter = props.diameter || this.defaultDiameter var diameter = props.diameter || this.defaultDiameter
var img = iconFactory.iconForAddress(address, diameter, false) if (!isNode) {
container.appendChild(img) var img = iconFactory.iconForAddress(address, diameter, false)
container.appendChild(img)
}
} }

View File

@ -34,7 +34,7 @@ Network.prototype.render = function () {
} else if (providerName === 'mainnet') { } else if (providerName === 'mainnet') {
hoverText = 'Main Ethereum Network' hoverText = 'Main Ethereum Network'
iconName = 'ethereum-network' iconName = 'ethereum-network'
} else if (providerName === 'testnet') { } else if (providerName === 'ropsten') {
hoverText = 'Ropsten Test Network' hoverText = 'Ropsten Test Network'
iconName = 'ropsten-test-network' iconName = 'ropsten-test-network'
} else if (parseInt(networkNumber) === 3) { } else if (parseInt(networkNumber) === 3) {
@ -90,7 +90,7 @@ Network.prototype.render = function () {
h('.menu-icon.golden-square'), h('.menu-icon.golden-square'),
h('.network-name', { h('.network-name', {
style: { style: {
color: '#550077', color: '#e7a218',
}}, }},
'Rinkeby Test Net'), 'Rinkeby Test Net'),
]) ])

View File

@ -1,5 +1,4 @@
const Component = require('react').Component const Component = require('react').Component
const connect = require('react-redux').connect
const h = require('react-hyperscript') const h = require('react-hyperscript')
const inherits = require('util').inherits const inherits = require('util').inherits
const actions = require('../actions') const actions = require('../actions')
@ -20,12 +19,7 @@ const GWEI_FACTOR = new BN(1e9)
const MIN_GAS_PRICE_BN = MIN_GAS_PRICE_GWEI_BN.mul(GWEI_FACTOR) const MIN_GAS_PRICE_BN = MIN_GAS_PRICE_GWEI_BN.mul(GWEI_FACTOR)
const MIN_GAS_LIMIT_BN = new BN(21000) const MIN_GAS_LIMIT_BN = new BN(21000)
module.exports = connect(mapStateToProps)(PendingTx) module.exports = PendingTx
function mapStateToProps (state) {
return {}
}
inherits(PendingTx, Component) inherits(PendingTx, Component)
function PendingTx () { function PendingTx () {
Component.call(this) Component.call(this)
@ -37,7 +31,9 @@ function PendingTx () {
PendingTx.prototype.render = function () { PendingTx.prototype.render = function () {
const props = this.props const props = this.props
const { currentCurrency } = props
const conversionRate = props.conversionRate
const txMeta = this.gatherTxMeta() const txMeta = this.gatherTxMeta()
const txParams = txMeta.txParams || {} const txParams = txMeta.txParams || {}
@ -61,7 +57,6 @@ PendingTx.prototype.render = function () {
const maxCost = txFeeBn.add(valueBn) const maxCost = txFeeBn.add(valueBn)
const dataLength = txParams.data ? (txParams.data.length - 2) / 2 : 0 const dataLength = txParams.data ? (txParams.data.length - 2) / 2 : 0
const imageify = props.imageifyIdenticons === undefined ? true : props.imageifyIdenticons
const balanceBn = hexToBn(balance) const balanceBn = hexToBn(balance)
const insufficientBalance = balanceBn.lt(maxCost) const insufficientBalance = balanceBn.lt(maxCost)
@ -75,18 +70,8 @@ PendingTx.prototype.render = function () {
}, [ }, [
h('form#pending-tx-form', { h('form#pending-tx-form', {
onSubmit: (event) => { onSubmit: this.onSubmit.bind(this),
const txMeta = this.gatherTxMeta()
event.preventDefault()
const form = document.querySelector('form#pending-tx-form')
const valid = form.checkValidity()
this.setState({ valid })
if (valid && this.verifyGasParams()) {
props.sendTransaction(txMeta, event)
} else {
this.props.dispatch(actions.displayWarning('Invalid Gas Parameters'))
}
},
}, [ }, [
// tx info // tx info
@ -100,7 +85,6 @@ PendingTx.prototype.render = function () {
h(MiniAccountPanel, { h(MiniAccountPanel, {
imageSeed: address, imageSeed: address,
imageifyIdenticons: imageify,
picOrder: 'right', picOrder: 'right',
}, [ }, [
h('span.font-small', { h('span.font-small', {
@ -121,6 +105,8 @@ PendingTx.prototype.render = function () {
}, [ }, [
h(EthBalance, { h(EthBalance, {
value: balance, value: balance,
conversionRate,
currentCurrency,
inline: true, inline: true,
labelColor: '#F7861C', labelColor: '#F7861C',
}), }),
@ -158,7 +144,7 @@ PendingTx.prototype.render = function () {
// in the way that gas and gasLimit currently are. // in the way that gas and gasLimit currently are.
h('.row', [ h('.row', [
h('.cell.label', 'Amount'), h('.cell.label', 'Amount'),
h(EthBalance, { value: txParams.value }), h(EthBalance, { value: txParams.value, currentCurrency, conversionRate }),
]), ]),
// Gas Limit (customizable) // Gas Limit (customizable)
@ -176,12 +162,8 @@ PendingTx.prototype.render = function () {
position: 'relative', position: 'relative',
top: '5px', top: '5px',
}, },
onChange: (newHex) => { onChange: this.gasLimitChanged.bind(this),
log.info(`Gas limit changed to ${newHex}`)
const txMeta = this.gatherTxMeta()
txMeta.txParams.gas = newHex
this.setState({ txData: txMeta })
},
ref: (hexInput) => { this.inputs.push(hexInput) }, ref: (hexInput) => { this.inputs.push(hexInput) },
}), }),
]), ]),
@ -201,13 +183,7 @@ PendingTx.prototype.render = function () {
position: 'relative', position: 'relative',
top: '5px', top: '5px',
}, },
onChange: (newHex) => { onChange: this.gasPriceChanged.bind(this),
log.info(`Gas price changed to: ${newHex}`)
const inWei = hexToBn(newHex).mul(GWEI_FACTOR)
const txMeta = this.gatherTxMeta()
txMeta.txParams.gasPrice = inWei.toString(16)
this.setState({ txData: txMeta })
},
ref: (hexInput) => { this.inputs.push(hexInput) }, ref: (hexInput) => { this.inputs.push(hexInput) },
}), }),
]), ]),
@ -216,7 +192,7 @@ PendingTx.prototype.render = function () {
// Max Transaction Fee (calculated) // Max Transaction Fee (calculated)
h('.cell.row', [ h('.cell.row', [
h('.cell.label', 'Max Transaction Fee'), h('.cell.label', 'Max Transaction Fee'),
h(EthBalance, { value: txFeeBn.toString(16) }), h(EthBalance, { value: txFeeBn.toString(16), currentCurrency, conversionRate }),
]), ]),
h('.cell.row', { h('.cell.row', {
@ -235,6 +211,8 @@ PendingTx.prototype.render = function () {
}, [ }, [
h(EthBalance, { h(EthBalance, {
value: maxCost.toString(16), value: maxCost.toString(16),
currentCurrency,
conversionRate,
inline: true, inline: true,
labelColor: 'black', labelColor: 'black',
fontSize: '16px', fontSize: '16px',
@ -331,13 +309,11 @@ PendingTx.prototype.miniAccountPanelForRecipient = function () {
const txData = props.txData const txData = props.txData
const txParams = txData.txParams || {} const txParams = txData.txParams || {}
const isContractDeploy = !('to' in txParams) const isContractDeploy = !('to' in txParams)
const imageify = props.imageifyIdenticons === undefined ? true : props.imageifyIdenticons
// If it's not a contract deploy, send to the account // If it's not a contract deploy, send to the account
if (!isContractDeploy) { if (!isContractDeploy) {
return h(MiniAccountPanel, { return h(MiniAccountPanel, {
imageSeed: txParams.to, imageSeed: txParams.to,
imageifyIdenticons: imageify,
picOrder: 'left', picOrder: 'left',
}, [ }, [
h('span.font-small', { h('span.font-small', {
@ -353,7 +329,6 @@ PendingTx.prototype.miniAccountPanelForRecipient = function () {
]) ])
} else { } else {
return h(MiniAccountPanel, { return h(MiniAccountPanel, {
imageifyIdenticons: imageify,
picOrder: 'left', picOrder: 'left',
}, [ }, [
@ -367,6 +342,21 @@ PendingTx.prototype.miniAccountPanelForRecipient = function () {
} }
} }
PendingTx.prototype.gasPriceChanged = function (newHex) {
log.info(`Gas price changed to: ${newHex}`)
const inWei = hexToBn(newHex).mul(GWEI_FACTOR)
const txMeta = this.gatherTxMeta()
txMeta.txParams.gasPrice = inWei.toString(16)
this.setState({ txData: txMeta })
}
PendingTx.prototype.gasLimitChanged = function (newHex) {
log.info(`Gas limit changed to ${newHex}`)
const txMeta = this.gatherTxMeta()
txMeta.txParams.gas = newHex
this.setState({ txData: txMeta })
}
PendingTx.prototype.resetGasFields = function () { PendingTx.prototype.resetGasFields = function () {
log.debug(`pending-tx resetGasFields`) log.debug(`pending-tx resetGasFields`)
@ -382,6 +372,33 @@ PendingTx.prototype.resetGasFields = function () {
}) })
} }
PendingTx.prototype.onSubmit = function (event) {
event.preventDefault()
const txMeta = this.gatherTxMeta()
const valid = this.checkValidity()
this.setState({ valid })
if (valid && this.verifyGasParams()) {
this.props.sendTransaction(txMeta, event)
} else {
this.props.dispatch(actions.displayWarning('Invalid Gas Parameters'))
}
}
PendingTx.prototype.checkValidity = function () {
const form = this.getFormEl()
const valid = form.checkValidity()
return valid
}
PendingTx.prototype.getFormEl = function () {
const form = document.querySelector('form#pending-tx-form')
// Stub out form for unit tests:
if (!form) {
return { checkValidity () { return true } }
}
return form
}
// After a customizable state value has been updated, // After a customizable state value has been updated,
PendingTx.prototype.gatherTxMeta = function () { PendingTx.prototype.gatherTxMeta = function () {
log.debug(`pending-tx gatherTxMeta`) log.debug(`pending-tx gatherTxMeta`)

View File

@ -8,14 +8,17 @@ const actions = require('../actions')
const addressSummary = require('../util').addressSummary const addressSummary = require('../util').addressSummary
const CopyButton = require('./copyButton') const CopyButton = require('./copyButton')
const EtherBalance = require('./eth-balance') const EthBalance = require('./eth-balance')
const Tooltip = require('./tooltip') const Tooltip = require('./tooltip')
module.exports = connect(mapStateToProps)(ShiftListItem) module.exports = connect(mapStateToProps)(ShiftListItem)
function mapStateToProps (state) { function mapStateToProps (state) {
return {} return {
conversionRate: state.metamask.conversionRate,
currentCurrency: state.metamask.currentCurrency,
}
} }
inherits(ShiftListItem, Component) inherits(ShiftListItem, Component)
@ -64,6 +67,7 @@ function formatDate (date) {
ShiftListItem.prototype.renderUtilComponents = function () { ShiftListItem.prototype.renderUtilComponents = function () {
var props = this.props var props = this.props
const { conversionRate, currentCurrency } = props
switch (props.response.status) { switch (props.response.status) {
case 'no_deposits': case 'no_deposits':
@ -94,8 +98,10 @@ ShiftListItem.prototype.renderUtilComponents = function () {
h(CopyButton, { h(CopyButton, {
value: this.props.response.transaction, value: this.props.response.transaction,
}), }),
h(EtherBalance, { h(EthBalance, {
value: `${props.response.outgoingCoin}`, value: `${props.response.outgoingCoin}`,
conversionRate,
currentCurrency,
width: '55px', width: '55px',
shorten: true, shorten: true,
needsParse: false, needsParse: false,

View File

@ -2,7 +2,7 @@ const Component = require('react').Component
const h = require('react-hyperscript') const h = require('react-hyperscript')
const inherits = require('util').inherits const inherits = require('util').inherits
const EtherBalance = require('./eth-balance') const EthBalance = require('./eth-balance')
const addressSummary = require('../util').addressSummary const addressSummary = require('../util').addressSummary
const explorerLink = require('../../lib/explorer-link') const explorerLink = require('../../lib/explorer-link')
const CopyButton = require('./copyButton') const CopyButton = require('./copyButton')
@ -19,7 +19,7 @@ function TransactionListItem () {
} }
TransactionListItem.prototype.render = function () { TransactionListItem.prototype.render = function () {
const { transaction, network } = this.props const { transaction, network, conversionRate, currentCurrency } = this.props
if (transaction.key === 'shapeshift') { if (transaction.key === 'shapeshift') {
if (network === '1') return h(ShiftListItem, transaction) if (network === '1') return h(ShiftListItem, transaction)
} }
@ -78,8 +78,10 @@ TransactionListItem.prototype.render = function () {
// Places a copy button if tx is successful, else places a placeholder empty div. // Places a copy button if tx is successful, else places a placeholder empty div.
transaction.hash ? h(CopyButton, { value: transaction.hash }) : h('div', {style: { display: 'flex', alignItems: 'center', width: '26px' }}), transaction.hash ? h(CopyButton, { value: transaction.hash }) : h('div', {style: { display: 'flex', alignItems: 'center', width: '26px' }}),
isTx ? h(EtherBalance, { isTx ? h(EthBalance, {
value: txParams.value, value: txParams.value,
conversionRate,
currentCurrency,
width: '55px', width: '55px',
shorten: true, shorten: true,
showFiat: false, showFiat: false,

View File

@ -13,7 +13,7 @@ function TransactionList () {
} }
TransactionList.prototype.render = function () { TransactionList.prototype.render = function () {
const { transactions, network, unapprovedMsgs } = this.props const { transactions, network, unapprovedMsgs, conversionRate } = this.props
var shapeShiftTxList var shapeShiftTxList
if (network === '1') { if (network === '1') {
@ -69,6 +69,7 @@ TransactionList.prototype.render = function () {
} }
return h(TransactionListItem, { return h(TransactionListItem, {
transaction, i, network, key, transaction, i, network, key,
conversionRate,
showTx: (txId) => { showTx: (txId) => {
this.props.viewPendingTx(txId) this.props.viewPendingTx(txId)
}, },

View File

@ -27,6 +27,8 @@ function mapStateToProps (state) {
warning: state.appState.warning, warning: state.appState.warning,
network: state.metamask.network, network: state.metamask.network,
provider: state.metamask.provider, provider: state.metamask.provider,
conversionRate: state.metamask.conversionRate,
currentCurrency: state.metamask.currentCurrency,
} }
} }
@ -37,8 +39,8 @@ function ConfirmTxScreen () {
ConfirmTxScreen.prototype.render = function () { ConfirmTxScreen.prototype.render = function () {
const props = this.props const props = this.props
const { network, provider, unapprovedTxs, const { network, provider, unapprovedTxs, currentCurrency,
unapprovedMsgs, unapprovedPersonalMsgs } = props unapprovedMsgs, unapprovedPersonalMsgs, conversionRate } = props
var unconfTxList = txHelper(unapprovedTxs, unapprovedMsgs, unapprovedPersonalMsgs, network) var unconfTxList = txHelper(unapprovedTxs, unapprovedMsgs, unapprovedPersonalMsgs, network)
@ -102,9 +104,11 @@ ConfirmTxScreen.prototype.render = function () {
selectedAddress: props.selectedAddress, selectedAddress: props.selectedAddress,
accounts: props.accounts, accounts: props.accounts,
identities: props.identities, identities: props.identities,
conversionRate,
currentCurrency,
// Actions // Actions
buyEth: this.buyEth.bind(this, txParams.from || props.selectedAddress), buyEth: this.buyEth.bind(this, txParams.from || props.selectedAddress),
sendTransaction: this.sendTransaction.bind(this, txData), sendTransaction: this.sendTransaction.bind(this),
cancelTransaction: this.cancelTransaction.bind(this, txData), cancelTransaction: this.cancelTransaction.bind(this, txData),
signMessage: this.signMessage.bind(this, txData), signMessage: this.signMessage.bind(this, txData),
signPersonalMessage: this.signPersonalMessage.bind(this, txData), signPersonalMessage: this.signPersonalMessage.bind(this, txData),

View File

@ -156,7 +156,7 @@ function currentProviderDisplay (metamaskState) {
value = 'Main Ethereum Network' value = 'Main Ethereum Network'
break break
case 'testnet': case 'ropsten':
title = 'Current Network' title = 'Current Network'
value = 'Ropsten Test Network' value = 'Ropsten Test Network'
break break

File diff suppressed because it is too large Load Diff

View File

@ -21,6 +21,8 @@ function mapStateToProps (state) {
warning: state.appState.warning, warning: state.appState.warning,
network: state.metamask.network, network: state.metamask.network,
addressBook: state.metamask.addressBook, addressBook: state.metamask.addressBook,
conversionRate: state.metamask.conversionRate,
currentCurrency: state.metamask.currentCurrency,
} }
result.error = result.warning && result.warning.split('.')[0] result.error = result.warning && result.warning.split('.')[0]
@ -40,13 +42,17 @@ function SendTransactionScreen () {
SendTransactionScreen.prototype.render = function () { SendTransactionScreen.prototype.render = function () {
this.persistentFormParentId = 'send-tx-form' this.persistentFormParentId = 'send-tx-form'
var state = this.props const props = this.props
var address = state.address const {
var account = state.account address,
var identity = state.identity account,
var network = state.network identity,
var identities = state.identities network,
var addressBook = state.addressBook identities,
addressBook,
conversionRate,
currentCurrency,
} = props
return ( return (
@ -125,6 +131,8 @@ SendTransactionScreen.prototype.render = function () {
h(EthBalance, { h(EthBalance, {
value: account && account.balance, value: account && account.balance,
conversionRate,
currentCurrency,
}), }),
]), ]),
@ -147,7 +155,7 @@ SendTransactionScreen.prototype.render = function () {
]), ]),
// error message // error message
state.error && h('span.error.flex-center', state.error), props.error && h('span.error.flex-center', props.error),
// 'to' field // 'to' field
h('section.flex-row.flex-center', [ h('section.flex-row.flex-center', [