aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorfrankiebee <frankie.diamond@gmail.com>2017-11-08 01:29:47 +0800
committerfrankiebee <frankie.diamond@gmail.com>2017-11-08 01:29:47 +0800
commit08867874ccea02dab9755ba6b0fc4221bb1dbf00 (patch)
tree5b2b893c4c00a14daa9724ba28ff7dc8f504ea10
parenta89b682dec350218b2d753d6cfb196f965acdde2 (diff)
parentacc973d543ac65f8db980c0007c248c509345411 (diff)
downloadtangerine-wallet-browser-08867874ccea02dab9755ba6b0fc4221bb1dbf00.tar.gz
tangerine-wallet-browser-08867874ccea02dab9755ba6b0fc4221bb1dbf00.tar.zst
tangerine-wallet-browser-08867874ccea02dab9755ba6b0fc4221bb1dbf00.zip
Merge remote-tracking branch 'origin/uat' into mascara-first-screen
-rw-r--r--app/scripts/controllers/preferences.js2
-rw-r--r--package.json4
-rw-r--r--test/integration/lib/first-time.js35
-rw-r--r--test/integration/lib/mascara-first-time.js26
-rw-r--r--test/lib/shallow-with-store.js11
-rw-r--r--test/unit/components/balance-component-test.js29
-rw-r--r--test/unit/components/pending-tx-test.js92
-rw-r--r--test/unit/pending-tx-test.js1
-rw-r--r--test/unit/responsive/components/dropdown-test.js116
-rw-r--r--ui/app/accounts/import/private-key.js4
-rw-r--r--ui/app/actions.js57
-rw-r--r--ui/app/add-token.js23
-rw-r--r--ui/app/app.js9
-rw-r--r--ui/app/components/account-menu/index.js1
-rw-r--r--ui/app/components/customize-gas-modal/gas-modal-card.js11
-rw-r--r--ui/app/components/customize-gas-modal/gas-slider.js98
-rw-r--r--ui/app/components/customize-gas-modal/index.js20
-rw-r--r--ui/app/components/dropdowns/account-dropdown-mini.js13
-rw-r--r--ui/app/components/dropdowns/account-options-dropdown.js2
-rw-r--r--ui/app/components/dropdowns/account-selection-dropdown.js2
-rw-r--r--ui/app/components/dropdowns/components/account-dropdowns.js15
-rw-r--r--ui/app/components/dropdowns/components/dropdown.js1
-rw-r--r--ui/app/components/dropdowns/simple-dropdown.js3
-rw-r--r--ui/app/components/dropdowns/token-menu-dropdown.js6
-rw-r--r--ui/app/components/input-number.js26
-rw-r--r--ui/app/components/loading.js5
-rw-r--r--ui/app/components/modals/account-details-modal.js2
-rw-r--r--ui/app/components/modals/export-private-key-modal.js1
-rw-r--r--ui/app/components/modals/modal.js2
-rw-r--r--ui/app/components/modals/new-account-modal.js141
-rw-r--r--ui/app/components/modals/shapeshift-deposit-tx-modal.js2
-rw-r--r--ui/app/components/network.js2
-rw-r--r--ui/app/components/pending-tx/confirm-deploy-contract.js6
-rw-r--r--ui/app/components/pending-tx/confirm-send-ether.js6
-rw-r--r--ui/app/components/pending-tx/confirm-send-token.js12
-rw-r--r--ui/app/components/send-token/index.js9
-rw-r--r--ui/app/components/send/account-list-item.js2
-rw-r--r--ui/app/components/send/currency-display.js13
-rw-r--r--ui/app/components/send/eth-fee-display.js4
-rw-r--r--ui/app/components/send/from-dropdown.js13
-rw-r--r--ui/app/components/send/gas-fee-display-v2.js7
-rw-r--r--ui/app/components/send/memo-textarea.js64
-rw-r--r--ui/app/components/send/send-constants.js26
-rw-r--r--ui/app/components/send/send-utils.js5
-rw-r--r--ui/app/components/send/send-v2-container.js14
-rw-r--r--ui/app/components/send/to-autocomplete.js18
-rw-r--r--ui/app/components/send/usd-fee-display.js4
-rw-r--r--ui/app/components/signature-request.js40
-rw-r--r--ui/app/components/tab-bar.js7
-rw-r--r--ui/app/components/token-cell.js36
-rw-r--r--ui/app/components/token-list.js26
-rw-r--r--ui/app/components/transaction-list-item.js48
-rw-r--r--ui/app/components/tx-list-item.js90
-rw-r--r--ui/app/components/tx-list.js16
-rw-r--r--ui/app/conf-tx.js4
-rw-r--r--ui/app/conversion-util.js54
-rw-r--r--ui/app/css/itcss/components/editable-label.scss1
-rw-r--r--ui/app/css/itcss/components/request-signature.scss12
-rw-r--r--ui/app/main-container.js6
-rw-r--r--ui/app/reducers/app.js4
-rw-r--r--ui/app/reducers/metamask.js2
-rw-r--r--ui/app/send-v2.js16
-rw-r--r--ui/app/send.js1094
-rw-r--r--ui/app/token-util.js36
64 files changed, 1296 insertions, 1161 deletions
diff --git a/app/scripts/controllers/preferences.js b/app/scripts/controllers/preferences.js
index ecac40481..10004caad 100644
--- a/app/scripts/controllers/preferences.js
+++ b/app/scripts/controllers/preferences.js
@@ -46,8 +46,6 @@ class PreferencesController {
}
removeToken (rawAddress) {
- const address = normalizeAddress(rawAddress)
-
const tokens = this.store.getState().tokens
const updatedTokens = tokens.filter(token => token.address !== rawAddress)
diff --git a/package.json b/package.json
index 81d1c9674..fae0881b8 100644
--- a/package.json
+++ b/package.json
@@ -15,7 +15,7 @@
"test": "npm run lint && npm run test:coverage && npm run test:integration",
"test:unit": "METAMASK_ENV=test mocha --compilers js:babel-core/register --require test/helper.js --recursive \"test/unit/**/*.js\"",
"test:single": "METAMASK_ENV=test mocha --require test/helper.js",
- "test:integration": "npm run test:flat && npm run test:mascara",
+ "test:integration": "gulp build:scss && npm run test:flat && npm run test:mascara",
"test:coverage": "nyc npm run test:unit && npm run test:coveralls-upload",
"test:coveralls-upload": "if [ $COVERALLS_REPO_TOKEN ]; then nyc report --reporter=text-lcov | coveralls; fi",
"test:flat": "npm run test:flat:build && karma start test/flat.conf.js",
@@ -190,6 +190,7 @@
"enzyme": "^2.8.2",
"eslint-plugin-chai": "0.0.1",
"eslint-plugin-mocha": "^4.9.0",
+ "eslint-plugin-react": "^7.4.0",
"eth-json-rpc-middleware": "^1.2.7",
"fs-promise": "^2.0.3",
"gulp": "github:gulpjs/gulp#4.0",
@@ -226,6 +227,7 @@
"react-addons-test-utils": "^15.5.1",
"react-test-renderer": "^15.5.4",
"react-testutils-additions": "^15.2.0",
+ "redux-test-utils": "^0.1.3",
"sinon": "^4.0.0",
"stylelint-config-standard": "^17.0.0",
"tape": "^4.5.1",
diff --git a/test/integration/lib/first-time.js b/test/integration/lib/first-time.js
index 61b38897e..e59897713 100644
--- a/test/integration/lib/first-time.js
+++ b/test/integration/lib/first-time.js
@@ -40,7 +40,8 @@ async function runFirstTimeUsageTest(assert, done) {
// Scroll through terms
const title = app.find('h1').text()
- assert.equal(title, 'MetaMask', 'title screen')
+ // TODO Find where Metamask is getting added twice in the title
+ assert.equal(title, 'MetaMaskMetaMask', 'title screen')
// enter password
const pwBox = app.find('#password-box')[0]
@@ -66,17 +67,17 @@ async function runFirstTimeUsageTest(assert, done) {
await timeout(1000)
- const detail = app.find('.account-detail-section')[0]
+ const detail = app.find('.wallet-view')[0]
assert.ok(detail, 'Account detail section loaded.')
- const sandwich = app.find('.sandwich-expando')[0]
- sandwich.click()
+ await timeout(1000)
- await timeout()
+ const menu = app.find('.account-menu__icon')[0]
+ menu.click()
+
+ await timeout(1000)
- const menu = app.find('.menu-droppo')[0]
- const children = menu.children
- const lock = children[children.length - 2]
+ const lock = app.find('.account-menu__logout-button')[0]
assert.ok(lock, 'Lock menu item found')
lock.click()
@@ -90,36 +91,30 @@ async function runFirstTimeUsageTest(assert, done) {
await timeout(1000)
- const detail2 = app.find('.account-detail-section')[0]
+ const detail2 = app.find('.wallet-view')[0]
assert.ok(detail2, 'Account detail section loaded again.')
await timeout()
// open account settings dropdown
- const qrButton = app.find('.fa.fa-ellipsis-h')[0]
+ const qrButton = app.find('.wallet-view__details-button')[0]
qrButton.click()
await timeout(1000)
- // qr code item
- const qrButton2 = app.find('.dropdown-menu-item')[1]
- qrButton2.click()
-
- await timeout(1000)
-
- const qrHeader = app.find('.qr-header')[0]
- const qrContainer = app.find('#qr-container')[0]
+ const qrHeader = app.find('.editable-label__value')[0]
+ const qrContainer = app.find('.qr-wrapper')[0]
assert.equal(qrHeader.textContent, 'Account 1', 'Should show account label.')
assert.ok(qrContainer, 'QR Container found')
await timeout()
- const networkMenu = app.find('.network-indicator')[0]
+ const networkMenu = app.find('.network-component')[0]
networkMenu.click()
await timeout()
- const networkMenu2 = app.find('.network-indicator')[0]
+ const networkMenu2 = app.find('.menu-droppo')[0]
const children2 = networkMenu2.children
children2.length[3]
assert.ok(children2, 'All network options present')
diff --git a/test/integration/lib/mascara-first-time.js b/test/integration/lib/mascara-first-time.js
index 3398a5511..398ecea0e 100644
--- a/test/integration/lib/mascara-first-time.js
+++ b/test/integration/lib/mascara-first-time.js
@@ -102,14 +102,12 @@ async function runFirstTimeUsageTest (assert, done) {
app.find('.buy-ether__do-it-later').click()
await timeout(1000)
- const sandwich = app.find('.sandwich-expando')[0]
- sandwich.click()
+ const menu = app.find('.account-menu__icon')[0]
+ menu.click()
await timeout()
- const menu = app.find('.menu-droppo')[0]
- const children = menu.children
- const lock = children[children.length - 2]
+ const lock = app.find('.account-menu__logout-button')[0]
assert.ok(lock, 'Lock menu item found')
lock.click()
@@ -123,31 +121,25 @@ async function runFirstTimeUsageTest (assert, done) {
await timeout(1000)
- const detail2 = app.find('.account-detail-section')[0]
+ const detail2 = app.find('.wallet-view')[0]
assert.ok(detail2, 'Account detail section loaded again.')
await timeout()
// open account settings dropdown
- const qrButton = app.find('.fa.fa-ellipsis-h')[0]
+ const qrButton = app.find('.wallet-view__details-button')[0]
qrButton.click()
await timeout(1000)
- // qr code item
- const qrButton2 = app.find('.dropdown-menu-item')[1]
- qrButton2.click()
-
- await timeout(1000)
-
- const qrHeader = app.find('.qr-header')[0]
- const qrContainer = app.find('#qr-container')[0]
+ const qrHeader = app.find('.editable-label__value')[0]
+ const qrContainer = app.find('.qr-wrapper')[0]
assert.equal(qrHeader.textContent, 'Account 1', 'Should show account label.')
assert.ok(qrContainer, 'QR Container found')
await timeout()
- const networkMenu = app.find('.network-indicator')[0]
+ const networkMenu = app.find('.network-component')[0]
networkMenu.click()
await timeout()
@@ -164,4 +156,4 @@ function timeout (time) {
return new Promise((resolve, reject) => {
setTimeout(resolve, time || 1500)
})
-}
+} \ No newline at end of file
diff --git a/test/lib/shallow-with-store.js b/test/lib/shallow-with-store.js
new file mode 100644
index 000000000..411aa0455
--- /dev/null
+++ b/test/lib/shallow-with-store.js
@@ -0,0 +1,11 @@
+const shallow = require('enzyme').shallow
+
+module.exports = shallowWithStore
+
+function shallowWithStore (component, store) {
+ const context = {
+ store,
+ }
+
+ return shallow(component, { context })
+};
diff --git a/test/unit/components/balance-component-test.js b/test/unit/components/balance-component-test.js
index c32a8ab2b..a5fededc8 100644
--- a/test/unit/components/balance-component-test.js
+++ b/test/unit/components/balance-component-test.js
@@ -1,18 +1,31 @@
-var assert = require('assert')
-var BalanceComponent = require('../../../ui/app/components/balance-component')
+const assert = require('assert')
+const h = require('react-hyperscript')
+const { createMockStore } = require('redux-test-utils')
+const shallowWithStore = require('../../lib/shallow-with-store')
+const BalanceComponent = require('../../../ui/app/components/balance-component')
+const mockState = {
+ metamask: {
+ accounts: { abc: {} },
+ network: 1,
+ selectedAddress: 'abc',
+ }
+}
describe('BalanceComponent', function () {
let balanceComponent
-
+ let store
+ let component
beforeEach(function () {
- balanceComponent = new BalanceComponent()
+ store = createMockStore(mockState)
+ component = shallowWithStore(h(BalanceComponent), store)
+ balanceComponent = component.dive()
})
it('shows token balance and convert to fiat value based on conversion rate', function () {
const formattedBalance = '1.23 ETH'
- const tokenBalance = balanceComponent.getTokenBalance(formattedBalance, false)
- const fiatDisplayNumber = balanceComponent.getFiatDisplayNumber(formattedBalance, 2)
+ const tokenBalance = balanceComponent.instance().getTokenBalance(formattedBalance, false)
+ const fiatDisplayNumber = balanceComponent.instance().getFiatDisplayNumber(formattedBalance, 2)
assert.equal('1.23 ETH', tokenBalance)
assert.equal(2.46, fiatDisplayNumber)
@@ -21,8 +34,8 @@ describe('BalanceComponent', function () {
it('shows only the token balance when conversion rate is not available', function () {
const formattedBalance = '1.23 ETH'
- const tokenBalance = balanceComponent.getTokenBalance(formattedBalance, false)
- const fiatDisplayNumber = balanceComponent.getFiatDisplayNumber(formattedBalance, 0)
+ const tokenBalance = balanceComponent.instance().getTokenBalance(formattedBalance, false)
+ const fiatDisplayNumber = balanceComponent.instance().getFiatDisplayNumber(formattedBalance, 0)
assert.equal('1.23 ETH', tokenBalance)
assert.equal('N/A', fiatDisplayNumber)
diff --git a/test/unit/components/pending-tx-test.js b/test/unit/components/pending-tx-test.js
index 20feba2a3..97cac3216 100644
--- a/test/unit/components/pending-tx-test.js
+++ b/test/unit/components/pending-tx-test.js
@@ -1,18 +1,22 @@
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 { createMockStore } = require('redux-test-utils')
+const shallowWithStore = require('../../lib/shallow-with-store')
+
+const identities = { abc: {}, def: {} }
+const mockState = {
+ metamask: {
+ accounts: { abc: {} },
+ identities,
+ conversionRate: 10,
+ selectedAddress: 'abc',
}
+}
+describe('PendingTx', function () {
const gasPrice = '0x4A817C800' // 20 Gwei
const txData = {
'id': 5021615666270214,
@@ -29,55 +33,35 @@ describe('PendingTx', function () {
'gasLimitSpecified': false,
'estimatedGas': '0x5208',
}
+ const newGasPrice = '0x77359400'
+ const computedBalances = {}
+ computedBalances[Object.keys(identities)[0]] = {
+ ethBalance: '0x00000000000000056bc75e2d63100000',
+ }
+ const props = {
+ txData,
+ computedBalances,
+ 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.')
+ },
+ }
- it('should use updated values when edited.', function (done) {
- const renderer = ReactTestUtils.createRenderer()
- const newGasPrice = '0x77359400'
-
- const computedBalances = {}
- computedBalances[Object.keys(identities)[0]] = {
- ethBalance: '0x00000000000000056bc75e2d63100000',
- }
- const props = {
- identities,
- accounts: identities,
- txData,
- computedBalances,
- 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 },
- },
- })
+ let pendingTxComponent
+ let store
+ let component
+ beforeEach(function () {
+ store = createMockStore(mockState)
+ component = shallowWithStore(h(PendingTx, props), store)
+ pendingTxComponent = component
+ })
- 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)
- }
+ it('should render correctly', function (done) {
+ assert.equal(pendingTxComponent.props().identities, identities)
+ done()
})
})
diff --git a/test/unit/pending-tx-test.js b/test/unit/pending-tx-test.js
index 4b5170dfe..32117a194 100644
--- a/test/unit/pending-tx-test.js
+++ b/test/unit/pending-tx-test.js
@@ -12,6 +12,7 @@ const currentNetworkId = 42
const otherNetworkId = 36
const privKey = new Buffer('8718b9618a37d1fc78c436511fc6df3c8258d3250635bba617f33003270ec03e', 'hex')
+
describe('PendingTransactionTracker', function () {
let pendingTxTracker, txMeta, txMetaNoHash, txMetaNoRawTx, providerResultStub,
provider, txMeta3, txList, knownErrors
diff --git a/test/unit/responsive/components/dropdown-test.js b/test/unit/responsive/components/dropdown-test.js
index 3ad2c390e..932b6c752 100644
--- a/test/unit/responsive/components/dropdown-test.js
+++ b/test/unit/responsive/components/dropdown-test.js
@@ -1,40 +1,45 @@
-var assert = require('assert');
+const assert = require('assert');
-const additions = require('react-testutils-additions');
const h = require('react-hyperscript');
-const ReactTestUtils = require('react-addons-test-utils');
const sinon = require('sinon');
const path = require('path');
-const Dropdown = require(path.join(__dirname, '..', '..', '..', '..', 'ui', 'app', 'components', 'dropdown.js')).Dropdown;
-const DropdownMenuItem = require(path.join(__dirname, '..', '..', '..', '..', 'ui', 'app', 'components', 'dropdown.js')).DropdownMenuItem;
+const Dropdown = require(path.join(__dirname, '..', '..', '..', '..', 'ui', 'app', 'components', 'dropdowns', 'index.js')).Dropdown;
+
+const { createMockStore } = require('redux-test-utils')
+const shallowWithStore = require('../../../lib/shallow-with-store')
+
+const mockState = {
+ metamask: {
+ }
+}
describe('Dropdown components', function () {
let onClickOutside;
let closeMenu;
let onClick;
- let dropdownComponentProps;
- const renderer = ReactTestUtils.createRenderer()
+ let dropdownComponentProps = {
+ isOpen: true,
+ zIndex: 11,
+ onClickOutside,
+ style: {
+ position: 'absolute',
+ right: 0,
+ top: '36px',
+ },
+ innerStyle: {},
+ }
+
+ let dropdownComponent
+ let store
+ let component
beforeEach(function () {
onClickOutside = sinon.spy();
closeMenu = sinon.spy();
onClick = sinon.spy();
- dropdownComponentProps = {
- isOpen: true,
- zIndex: 11,
- onClickOutside,
- style: {
- position: 'absolute',
- right: 0,
- top: '36px',
- },
- innerStyle: {},
- }
- });
-
- it('can render two items', function () {
- const dropdownComponent = h(
+ store = createMockStore(mockState)
+ component = shallowWithStore(h(
Dropdown,
dropdownComponentProps,
[
@@ -42,74 +47,35 @@ describe('Dropdown components', function () {
.drop-menu-item:hover { background:rgb(235, 235, 235); }
.drop-menu-item i { margin: 11px; }
`),
- h(DropdownMenuItem, {
+ h('li', {
closeMenu,
onClick,
}, 'Item 1'),
- h(DropdownMenuItem, {
+ h('li', {
closeMenu,
onClick,
}, 'Item 2'),
]
- )
+ ), store)
+ dropdownComponent = component.dive()
+ })
- const component = additions.renderIntoDocument(dropdownComponent);
- renderer.render(dropdownComponent);
- const items = additions.find(component, 'li');
+ it('can render two items', function () {
+ const items = dropdownComponent.find('li');
assert.equal(items.length, 2);
});
it('closes when item clicked', function() {
- const dropdownComponent = h(
- Dropdown,
- dropdownComponentProps,
- [
- h('style', `
- .drop-menu-item:hover { background:rgb(235, 235, 235); }
- .drop-menu-item i { margin: 11px; }
- `),
- h(DropdownMenuItem, {
- closeMenu,
- onClick,
- }, 'Item 1'),
- h(DropdownMenuItem, {
- closeMenu,
- onClick,
- }, 'Item 2'),
- ]
- )
- const component = additions.renderIntoDocument(dropdownComponent);
- renderer.render(dropdownComponent);
- const items = additions.find(component, 'li');
- const node = items[0];
- ReactTestUtils.Simulate.click(node);
- assert.equal(closeMenu.calledOnce, true);
+ const items = dropdownComponent.find('li');
+ const node = items.at(0);
+ node.simulate('click');
+ assert.equal(node.props().closeMenu, closeMenu);
});
it('invokes click handler when item clicked', function() {
- const dropdownComponent = h(
- Dropdown,
- dropdownComponentProps,
- [
- h('style', `
- .drop-menu-item:hover { background:rgb(235, 235, 235); }
- .drop-menu-item i { margin: 11px; }
- `),
- h(DropdownMenuItem, {
- closeMenu,
- onClick,
- }, 'Item 1'),
- h(DropdownMenuItem, {
- closeMenu,
- onClick,
- }, 'Item 2'),
- ]
- )
- const component = additions.renderIntoDocument(dropdownComponent);
- renderer.render(dropdownComponent);
- const items = additions.find(component, 'li');
- const node = items[0];
- ReactTestUtils.Simulate.click(node);
+ const items = dropdownComponent.find('li');
+ const node = items.at(0);
+ node.simulate('click');
assert.equal(onClick.calledOnce, true);
});
});
diff --git a/ui/app/accounts/import/private-key.js b/ui/app/accounts/import/private-key.js
index 68ccee58e..e214bcbbe 100644
--- a/ui/app/accounts/import/private-key.js
+++ b/ui/app/accounts/import/private-key.js
@@ -17,6 +17,10 @@ function PrivateKeyImportView () {
Component.call(this)
}
+PrivateKeyImportView.prototype.componentWillUnmount = function () {
+ this.props.dispatch(actions.displayWarning(null))
+}
+
PrivateKeyImportView.prototype.render = function () {
const { error } = this.props
diff --git a/ui/app/actions.js b/ui/app/actions.js
index 2e9b34c58..5d3befa63 100644
--- a/ui/app/actions.js
+++ b/ui/app/actions.js
@@ -224,6 +224,8 @@ var actions = {
TOGGLE_ACCOUNT_MENU: 'TOGGLE_ACCOUNT_MENU',
toggleAccountMenu,
+
+ useEtherscanProvider,
}
module.exports = actions
@@ -428,7 +430,7 @@ function addNewAccount () {
forceUpdateMetamaskState(dispatch)
return resolve(newAccountAddress)
})
- });
+ })
}
}
@@ -619,7 +621,7 @@ function updateSendErrors (error) {
function clearSend () {
return {
- type: actions.CLEAR_SEND
+ type: actions.CLEAR_SEND,
}
}
@@ -808,9 +810,50 @@ function updateMetamaskState (newState) {
}
}
+const backgroundSetLocked = () => {
+ return new Promise((resolve, reject) => {
+ background.setLocked(error => {
+ if (error) {
+ return reject(error)
+ }
+
+ resolve()
+ })
+ })
+}
+
+const updateMetamaskStateFromBackground = () => {
+ log.debug(`background.getState`)
+
+ return new Promise((resolve, reject) => {
+ background.getState((error, newState) => {
+ if (error) {
+ return reject(error)
+ }
+
+ resolve(newState)
+ })
+ })
+}
+
function lockMetamask () {
log.debug(`background.setLocked`)
- return callBackgroundThenUpdate(background.setLocked)
+
+ return dispatch => {
+ dispatch(actions.showLoadingIndication())
+
+ return backgroundSetLocked()
+ .then(() => updateMetamaskStateFromBackground())
+ .catch(error => {
+ dispatch(actions.displayWarning(error.message))
+ return Promise.reject(error)
+ })
+ .then(newState => {
+ dispatch(actions.updateMetamaskState(newState))
+ dispatch({ type: actions.LOCK_METAMASK })
+ })
+ .catch(() => dispatch({ type: actions.LOCK_METAMASK }))
+ }
}
function setCurrentAccountTab (newTabName) {
@@ -961,10 +1004,10 @@ function addTokens (tokens) {
}
}
-function updateTokens(newTokens) {
+function updateTokens (newTokens) {
return {
type: actions.UPDATE_TOKENS,
- newTokens
+ newTokens,
}
}
@@ -1038,7 +1081,7 @@ function setProviderType (type) {
}
}
-function updateProviderType(type) {
+function updateProviderType (type) {
return {
type: actions.SET_PROVIDER_TYPE,
value: type,
@@ -1196,7 +1239,7 @@ function exportAccount (password, address) {
}
}
-function exportAccountComplete() {
+function exportAccountComplete () {
return {
type: actions.EXPORT_ACCOUNT,
}
diff --git a/ui/app/add-token.js b/ui/app/add-token.js
index 518701a1d..10aaae103 100644
--- a/ui/app/add-token.js
+++ b/ui/app/add-token.js
@@ -21,9 +21,7 @@ const fuse = new Fuse(contractList, {
})
const actions = require('./actions')
const ethUtil = require('ethereumjs-util')
-const abi = require('human-standard-token-abi')
-const Eth = require('ethjs-query')
-const EthContract = require('ethjs-contract')
+const { tokenInfoGetter } = require('./token-util')
const R = require('ramda')
const emptyAddr = '0x0000000000000000000000000000000000000000'
@@ -63,11 +61,7 @@ function AddTokenScreen () {
}
AddTokenScreen.prototype.componentWillMount = function () {
- if (typeof global.ethereumProvider === 'undefined') return
-
- this.eth = new Eth(global.ethereumProvider)
- this.contract = new EthContract(this.eth)
- this.TokenContract = this.contract(abi)
+ this.tokenInfoGetter = tokenInfoGetter()
}
AddTokenScreen.prototype.toggleToken = function (address, token) {
@@ -164,18 +158,11 @@ AddTokenScreen.prototype.validate = function () {
}
AddTokenScreen.prototype.attemptToAutoFillTokenParams = async function (address) {
- const contract = this.TokenContract.at(address)
-
- const results = await Promise.all([
- contract.symbol(),
- contract.decimals(),
- ])
-
- const [ symbol, decimals ] = results
+ const { symbol, decimals } = await this.tokenInfoGetter(address)
if (symbol && decimals) {
this.setState({
- customSymbol: symbol[0],
- customDecimals: decimals[0].toString(),
+ customSymbol: symbol,
+ customDecimals: decimals.toString(),
})
}
}
diff --git a/ui/app/app.js b/ui/app/app.js
index 7264c79c7..e90c3e98e 100644
--- a/ui/app/app.js
+++ b/ui/app/app.js
@@ -2,7 +2,6 @@ const inherits = require('util').inherits
const Component = require('react').Component
const connect = require('react-redux').connect
const h = require('react-hyperscript')
-const { checkFeatureToggle } = require('../lib/feature-toggle-utils')
const actions = require('./actions')
// mascara
const MascaraFirstTime = require('../../mascara/src/app/first-time').default
@@ -12,9 +11,7 @@ const InitializeMenuScreen = require('./first-time/init-menu')
const NewKeyChainScreen = require('./new-keychain')
// accounts
const MainContainer = require('./main-container')
-const SendTransactionScreen = require('./send')
const SendTransactionScreen2 = require('./components/send/send-v2-container')
-const SendTokenScreen = require('./components/send-token')
const ConfirmTxScreen = require('./conf-tx')
// notice
const NoticeScreen = require('./components/notice')
@@ -27,7 +24,6 @@ const WalletView = require('./components/wallet-view')
const Settings = require('./settings')
const AddTokenScreen = require('./add-token')
const Import = require('./accounts/import')
-const InfoScreen = require('./info')
const Loading = require('./components/loading')
const NetworkIndicator = require('./components/network')
const Identicon = require('./components/identicon')
@@ -38,6 +34,7 @@ const RevealSeedConfirmation = require('./keychains/hd/recover-seed/confirmation
const ReactCSSTransitionGroup = require('react-addons-css-transition-group')
const NetworkDropdown = require('./components/dropdowns/network-dropdown')
const AccountMenu = require('./components/account-menu')
+const QrView = require('./components/qr-code')
// Global Modals
const Modal = require('./components/modals/index').Modal
@@ -148,7 +145,7 @@ App.prototype.render = function () {
(isLoading || isLoadingNetwork) && h(Loading, {
loadingMessage: loadMessage,
}),
-
+
// this.renderLoadingIndicator({ isLoading, isLoadingNetwork, loadMessage }),
// content
@@ -228,8 +225,6 @@ App.prototype.renderAppBar = function () {
}
const props = this.props
- const state = this.state || {}
- const isNetworkMenuOpen = state.isNetworkMenuOpen || false
const {isMascara, isOnboarding} = props
// Do not render header if user is in mascara onboarding
diff --git a/ui/app/components/account-menu/index.js b/ui/app/components/account-menu/index.js
index 38c7bcb2d..a9f075ec7 100644
--- a/ui/app/components/account-menu/index.js
+++ b/ui/app/components/account-menu/index.js
@@ -32,6 +32,7 @@ function mapDispatchToProps (dispatch) {
},
lockMetamask: () => {
dispatch(actions.lockMetamask())
+ dispatch(actions.displayWarning(null))
dispatch(actions.toggleAccountMenu())
},
showConfigPage: () => {
diff --git a/ui/app/components/customize-gas-modal/gas-modal-card.js b/ui/app/components/customize-gas-modal/gas-modal-card.js
index de181dc67..23754d819 100644
--- a/ui/app/components/customize-gas-modal/gas-modal-card.js
+++ b/ui/app/components/customize-gas-modal/gas-modal-card.js
@@ -2,7 +2,7 @@ const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
const InputNumber = require('../input-number.js')
-const GasSlider = require('./gas-slider.js')
+// const GasSlider = require('./gas-slider.js')
module.exports = GasModalCard
@@ -13,8 +13,7 @@ function GasModalCard () {
GasModalCard.prototype.render = function () {
const {
- memo,
- identities,
+ // memo,
onChange,
unitLabel,
value,
@@ -22,7 +21,7 @@ GasModalCard.prototype.render = function () {
// max,
step,
title,
- copy
+ copy,
} = this.props
return h('div.send-v2__gas-modal-card', [
@@ -48,8 +47,8 @@ GasModalCard.prototype.render = function () {
// min,
// onChange,
// }),
-
+
])
-
+
}
diff --git a/ui/app/components/customize-gas-modal/gas-slider.js b/ui/app/components/customize-gas-modal/gas-slider.js
index e76e96545..69fd6f985 100644
--- a/ui/app/components/customize-gas-modal/gas-slider.js
+++ b/ui/app/components/customize-gas-modal/gas-slider.js
@@ -1,50 +1,50 @@
-const Component = require('react').Component
-const h = require('react-hyperscript')
-const inherits = require('util').inherits
-
-module.exports = GasSlider
-
-inherits(GasSlider, Component)
-function GasSlider () {
- Component.call(this)
-}
-
-GasSlider.prototype.render = function () {
- const {
- memo,
- identities,
- onChange,
- unitLabel,
- value,
- id,
- step,
- max,
- min,
- } = this.props
-
- return h('div.gas-slider', [
-
- h('input.gas-slider__input', {
- type: 'range',
- step,
- max,
- min,
- value,
- id: 'gasSlider',
- onChange: event => onChange(event.target.value),
- }, []),
-
- h('div.gas-slider__bar', [
-
- h('div.gas-slider__low'),
-
- h('div.gas-slider__mid'),
-
- h('div.gas-slider__high'),
-
- ]),
-
- ])
-
-}
+// const Component = require('react').Component
+// const h = require('react-hyperscript')
+// const inherits = require('util').inherits
+
+// module.exports = GasSlider
+
+// inherits(GasSlider, Component)
+// function GasSlider () {
+// Component.call(this)
+// }
+
+// GasSlider.prototype.render = function () {
+// const {
+// memo,
+// identities,
+// onChange,
+// unitLabel,
+// value,
+// id,
+// step,
+// max,
+// min,
+// } = this.props
+
+// return h('div.gas-slider', [
+
+// h('input.gas-slider__input', {
+// type: 'range',
+// step,
+// max,
+// min,
+// value,
+// id: 'gasSlider',
+// onChange: event => onChange(event.target.value),
+// }, []),
+
+// h('div.gas-slider__bar', [
+
+// h('div.gas-slider__low'),
+
+// h('div.gas-slider__mid'),
+
+// h('div.gas-slider__high'),
+
+// ]),
+
+// ])
+
+// }
diff --git a/ui/app/components/customize-gas-modal/index.js b/ui/app/components/customize-gas-modal/index.js
index 722ed2b23..101a19d9f 100644
--- a/ui/app/components/customize-gas-modal/index.js
+++ b/ui/app/components/customize-gas-modal/index.js
@@ -58,7 +58,7 @@ function mapDispatchToProps (dispatch) {
}
}
-function getOriginalState(props) {
+function getOriginalState (props) {
const gasPrice = props.gasPrice || MIN_GAS_PRICE_DEC
const gasLimit = props.gasLimit || MIN_GAS_LIMIT_DEC
@@ -90,7 +90,7 @@ CustomizeGasModal.prototype.save = function (gasPrice, gasLimit, gasTotal) {
updateGasPrice,
updateGasLimit,
hideModal,
- updateGasTotal
+ updateGasTotal,
} = this.props
updateGasPrice(gasPrice)
@@ -126,9 +126,9 @@ CustomizeGasModal.prototype.validate = function ({ gasTotal, gasLimit }) {
})
if (!balanceIsSufficient) {
- error = 'Insufficient balance for current gas total'
+ error = 'Insufficient balance for current gas total'
}
-
+
const gasLimitTooLow = gasLimit && conversionGreaterThan(
{
value: MIN_GAS_LIMIT_DEC,
@@ -142,7 +142,7 @@ CustomizeGasModal.prototype.validate = function ({ gasTotal, gasLimit }) {
)
if (gasLimitTooLow) {
- error = 'Gas limit must be at least 21000'
+ error = 'Gas limit must be at least 21000'
}
this.setState({ error })
@@ -190,7 +190,7 @@ CustomizeGasModal.prototype.convertAndSetGasPrice = function (newGasPrice) {
}
CustomizeGasModal.prototype.render = function () {
- const { hideModal, conversionRate } = this.props
+ const { hideModal } = this.props
const { gasPrice, gasLimit, gasTotal, error } = this.state
const convertedGasPrice = conversionUtil(gasPrice, {
@@ -219,12 +219,12 @@ CustomizeGasModal.prototype.render = function () {
]),
h('div.send-v2__customize-gas__body', {}, [
-
+
h(GasModalCard, {
value: convertedGasPrice,
min: MIN_GAS_PRICE_GWEI,
// max: 1000,
- step: 1,
+ step: MIN_GAS_PRICE_GWEI,
onChange: value => this.convertAndSetGasPrice(value),
title: 'Gas Price (GWEI)',
copy: 'We calculate the suggested gas prices based on network success rates.',
@@ -247,7 +247,7 @@ CustomizeGasModal.prototype.render = function () {
error && h('div.send-v2__customize-gas__error-message', [
error,
]),
-
+
h('div.send-v2__customize-gas__revert', {
onClick: () => this.revert(),
}, ['Revert']),
@@ -260,7 +260,7 @@ CustomizeGasModal.prototype.render = function () {
h(`div.send-v2__customize-gas__save${error ? '__error' : ''}`, {
onClick: () => !error && this.save(gasPrice, gasLimit, gasTotal),
}, ['SAVE']),
- ])
+ ]),
]),
diff --git a/ui/app/components/dropdowns/account-dropdown-mini.js b/ui/app/components/dropdowns/account-dropdown-mini.js
index 96057d2b4..a3d41af90 100644
--- a/ui/app/components/dropdowns/account-dropdown-mini.js
+++ b/ui/app/components/dropdowns/account-dropdown-mini.js
@@ -1,7 +1,6 @@
const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
-const Identicon = require('../identicon')
const AccountListItem = require('../send/account-list-item')
module.exports = AccountDropdownMini
@@ -38,13 +37,13 @@ AccountDropdownMini.prototype.renderDropdown = function () {
...accounts.map(account => h(AccountListItem, {
account,
displayBalance: false,
- displayAddress: false,
+ displayAddress: false,
handleClick: () => {
onSelect(account)
closeDropdown()
- },
+ },
icon: this.getListItemIcon(account, selectedAccount),
- }))
+ })),
]),
@@ -53,10 +52,8 @@ AccountDropdownMini.prototype.renderDropdown = function () {
AccountDropdownMini.prototype.render = function () {
const {
- accounts,
selectedAccount,
openDropdown,
- closeDropdown,
dropdownOpen,
} = this.props
@@ -67,12 +64,12 @@ AccountDropdownMini.prototype.render = function () {
handleClick: openDropdown,
displayBalance: false,
displayAddress: false,
- icon: h(`i.fa.fa-caret-down.fa-lg`, { style: { color: '#dedede' } })
+ icon: h(`i.fa.fa-caret-down.fa-lg`, { style: { color: '#dedede' } }),
}),
dropdownOpen && this.renderDropdown(),
])
-
+
}
diff --git a/ui/app/components/dropdowns/account-options-dropdown.js b/ui/app/components/dropdowns/account-options-dropdown.js
index 50e793d87..f74c0a2d4 100644
--- a/ui/app/components/dropdowns/account-options-dropdown.js
+++ b/ui/app/components/dropdowns/account-options-dropdown.js
@@ -19,7 +19,7 @@ AccountOptionsDropdown.prototype.render = function () {
return h(AccountDropdowns, {
enableAccountOptions: true,
enableAccountsSelector: false,
- selected: selectedAddress,
+ selected,
network,
identities,
style: style || {},
diff --git a/ui/app/components/dropdowns/account-selection-dropdown.js b/ui/app/components/dropdowns/account-selection-dropdown.js
index 7a8502d18..2f6452b15 100644
--- a/ui/app/components/dropdowns/account-selection-dropdown.js
+++ b/ui/app/components/dropdowns/account-selection-dropdown.js
@@ -19,7 +19,7 @@ AccountSelectionDropdown.prototype.render = function () {
return h(AccountDropdowns, {
enableAccountOptions: false,
enableAccountsSelector: true,
- selected: selectedAddress,
+ selected,
network,
identities,
style: style || {},
diff --git a/ui/app/components/dropdowns/components/account-dropdowns.js b/ui/app/components/dropdowns/components/account-dropdowns.js
index e2eed1e4b..58326b13c 100644
--- a/ui/app/components/dropdowns/components/account-dropdowns.js
+++ b/ui/app/components/dropdowns/components/account-dropdowns.js
@@ -425,6 +425,21 @@ AccountDropdowns.propTypes = {
identities: PropTypes.objectOf(PropTypes.object),
selected: PropTypes.string,
keyrings: PropTypes.array,
+ accounts: PropTypes.object,
+ menuItemStyles: PropTypes.object,
+ actions: PropTypes.object,
+ // actions.showAccountDetail: ,
+ useCssTransition: PropTypes.bool,
+ innerStyle: PropTypes.object,
+ sidebarOpen: PropTypes.bool,
+ dropdownWrapperStyle: PropTypes.string,
+ // actions.showAccountDetailModal: ,
+ network: PropTypes.number,
+ // actions.showExportPrivateKeyModal: ,
+ style: PropTypes.object,
+ enableAccountsSelector: PropTypes.bool,
+ enableAccountOption: PropTypes.bool,
+ enableAccountOptions: PropTypes.bool,
}
const mapDispatchToProps = (dispatch) => {
diff --git a/ui/app/components/dropdowns/components/dropdown.js b/ui/app/components/dropdowns/components/dropdown.js
index ca68e55f7..ddcb7998f 100644
--- a/ui/app/components/dropdowns/components/dropdown.js
+++ b/ui/app/components/dropdowns/components/dropdown.js
@@ -68,6 +68,7 @@ Dropdown.propTypes = {
onClickOutside: PropTypes.func,
innerStyle: PropTypes.object,
useCssTransition: PropTypes.bool,
+ containerClassName: PropTypes.string,
}
class DropdownMenuItem extends Component {
diff --git a/ui/app/components/dropdowns/simple-dropdown.js b/ui/app/components/dropdowns/simple-dropdown.js
index 8cea78518..7bb48e57b 100644
--- a/ui/app/components/dropdowns/simple-dropdown.js
+++ b/ui/app/components/dropdowns/simple-dropdown.js
@@ -1,4 +1,5 @@
-const { Component, PropTypes } = require('react')
+const { Component } = require('react')
+const PropTypes = require('react').PropTypes
const h = require('react-hyperscript')
const classnames = require('classnames')
const R = require('ramda')
diff --git a/ui/app/components/dropdowns/token-menu-dropdown.js b/ui/app/components/dropdowns/token-menu-dropdown.js
index 7234a9b21..dc7a985e3 100644
--- a/ui/app/components/dropdowns/token-menu-dropdown.js
+++ b/ui/app/components/dropdowns/token-menu-dropdown.js
@@ -10,7 +10,7 @@ function mapDispatchToProps (dispatch) {
return {
showHideTokenConfirmationModal: (token) => {
dispatch(actions.showModal({ name: 'HIDE_TOKEN_CONFIRMATION', token }))
- }
+ },
}
}
@@ -36,14 +36,14 @@ TokenMenuDropdown.prototype.render = function () {
}),
h('div.token-menu-dropdown__container', {}, [
h('div.token-menu-dropdown__options', {}, [
-
+
h('div.token-menu-dropdown__option', {
onClick: (e) => {
e.stopPropagation()
showHideTokenConfirmationModal(this.props.token)
this.props.onClose()
},
- }, 'Hide Token')
+ }, 'Hide Token'),
]),
]),
diff --git a/ui/app/components/input-number.js b/ui/app/components/input-number.js
index 16347fd5e..e28807c13 100644
--- a/ui/app/components/input-number.js
+++ b/ui/app/components/input-number.js
@@ -1,7 +1,12 @@
const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
-const { addCurrencies } = require('../conversion-util')
+const {
+ addCurrencies,
+ conversionGTE,
+ conversionLTE,
+ toNegative,
+} = require('../conversion-util')
module.exports = InputNumber
@@ -17,8 +22,21 @@ InputNumber.prototype.setValue = function (newValue) {
newValue = Number(fixed ? newValue.toFixed(4) : newValue)
- if (newValue >= min && newValue <= max) {
+ const newValueGreaterThanMin = conversionGTE(
+ { value: newValue, fromNumericBase: 'dec' },
+ { value: min, fromNumericBase: 'hex' },
+ )
+
+ const newValueLessThanMax = conversionLTE(
+ { value: newValue, fromNumericBase: 'dec' },
+ { value: max, fromNumericBase: 'hex' },
+ )
+ if (newValueGreaterThanMin && newValueLessThanMax) {
onChange(newValue)
+ } else if (!newValueGreaterThanMin) {
+ onChange(min)
+ } else if (!newValueLessThanMax) {
+ onChange(max)
}
}
@@ -29,7 +47,7 @@ InputNumber.prototype.render = function () {
h('input.customize-gas-input', {
placeholder,
type: 'number',
- value: value,
+ value,
onChange: (e) => this.setValue(e.target.value),
}),
h('span.gas-tooltip-input-detail', {}, [unitLabel]),
@@ -39,7 +57,7 @@ InputNumber.prototype.render = function () {
}),
h('i.fa.fa-angle-down', {
style: { cursor: 'pointer' },
- onClick: () => this.setValue(addCurrencies(value, step * -1)),
+ onClick: () => this.setValue(addCurrencies(value, toNegative(step))),
}),
]),
])
diff --git a/ui/app/components/loading.js b/ui/app/components/loading.js
index e6d841aa0..587212015 100644
--- a/ui/app/components/loading.js
+++ b/ui/app/components/loading.js
@@ -1,5 +1,6 @@
const { Component } = require('react')
const h = require('react-hyperscript')
+const PropTypes = require('react').PropTypes
class LoadingIndicator extends Component {
renderMessage () {
@@ -35,4 +36,8 @@ class LoadingIndicator extends Component {
}
}
+LoadingIndicator.propTypes = {
+ loadingMessage: PropTypes.string,
+}
+
module.exports = LoadingIndicator
diff --git a/ui/app/components/modals/account-details-modal.js b/ui/app/components/modals/account-details-modal.js
index e3c936702..4bf671834 100644
--- a/ui/app/components/modals/account-details-modal.js
+++ b/ui/app/components/modals/account-details-modal.js
@@ -4,7 +4,7 @@ const inherits = require('util').inherits
const connect = require('react-redux').connect
const actions = require('../../actions')
const AccountModalContainer = require('./account-modal-container')
-const { getSelectedIdentity, getSelectedAddress } = require('../../selectors')
+const { getSelectedIdentity } = require('../../selectors')
const genAccountLink = require('../../../lib/account-link.js')
const QrView = require('../qr-code')
const EditableLabel = require('../editable-label')
diff --git a/ui/app/components/modals/export-private-key-modal.js b/ui/app/components/modals/export-private-key-modal.js
index 2d8470634..193755df5 100644
--- a/ui/app/components/modals/export-private-key-modal.js
+++ b/ui/app/components/modals/export-private-key-modal.js
@@ -92,7 +92,6 @@ ExportPrivateKeyModal.prototype.renderButtons = function (privateKey, password,
ExportPrivateKeyModal.prototype.render = function () {
const {
selectedIdentity,
- network,
warning,
showAccountDetailModal,
hideModal,
diff --git a/ui/app/components/modals/modal.js b/ui/app/components/modals/modal.js
index e15dd6c1b..842081f40 100644
--- a/ui/app/components/modals/modal.js
+++ b/ui/app/components/modals/modal.js
@@ -220,7 +220,7 @@ Modal.prototype.render = function () {
const children = modal.contents
const modalStyle = modal[isMobileView() ? 'mobileModalStyle' : 'laptopModalStyle']
- const contentStyle = modal.contentStyle || {};
+ const contentStyle = modal.contentStyle || {}
return h(FadeModal,
{
diff --git a/ui/app/components/modals/new-account-modal.js b/ui/app/components/modals/new-account-modal.js
index b78de1d8d..fc1fd413d 100644
--- a/ui/app/components/modals/new-account-modal.js
+++ b/ui/app/components/modals/new-account-modal.js
@@ -1,17 +1,88 @@
-const Component = require('react').Component
+const { Component } = require('react')
+const PropTypes = require('prop-types')
const h = require('react-hyperscript')
-const inherits = require('util').inherits
-const connect = require('react-redux').connect
+const { connect } = require('react-redux')
const actions = require('../../actions')
-function mapStateToProps (state) {
+class NewAccountModal extends Component {
+ constructor (props) {
+ super(props)
+ const { numberOfExistingAccounts = 0 } = props
+ const newAccountNumber = numberOfExistingAccounts + 1
+
+ this.state = {
+ newAccountName: `Account ${newAccountNumber}`,
+ }
+ }
+
+ render () {
+ const { newAccountName } = this.state
+
+ return h('div', [
+ h('div.new-account-modal-wrapper', {
+ }, [
+ h('div.new-account-modal-header', {}, [
+ 'New Account',
+ ]),
+
+ h('div.modal-close-x', {
+ onClick: this.props.hideModal,
+ }),
+
+ h('div.new-account-modal-content', {}, [
+ 'Account Name',
+ ]),
+
+ h('div.new-account-input-wrapper', {}, [
+ h('input.new-account-input', {
+ value: this.state.newAccountName,
+ placeholder: 'E.g. My new account',
+ onChange: event => this.setState({ newAccountName: event.target.value }),
+ }, []),
+ ]),
+
+ h('div.new-account-modal-content.after-input', {}, [
+ 'or',
+ ]),
+
+ h('div.new-account-modal-content.after-input.pointer', {
+ onClick: () => {
+ this.props.hideModal()
+ this.props.showImportPage()
+ },
+ }, 'Import an account'),
+
+ h('div.new-account-modal-content.button', {}, [
+ h('button.btn-clear', {
+ onClick: () => this.props.createAccount(newAccountName),
+ }, [
+ 'SAVE',
+ ]),
+ ]),
+ ]),
+ ])
+ }
+}
+
+NewAccountModal.propTypes = {
+ hideModal: PropTypes.func,
+ showImportPage: PropTypes.func,
+ createAccount: PropTypes.func,
+ numberOfExistingAccounts: PropTypes.number,
+}
+
+const mapStateToProps = state => {
+ const { metamask: { network, selectedAddress, identities = {} } } = state
+ const numberOfExistingAccounts = Object.keys(identities).length
+
return {
- network: state.metamask.network,
- address: state.metamask.selectedAddress,
+ network,
+ address: selectedAddress,
+ numberOfExistingAccounts,
}
}
-function mapDispatchToProps (dispatch) {
+const mapDispatchToProps = dispatch => {
return {
toCoinbase: (address) => {
dispatch(actions.buyEth({ network: '1', address, amount: 0 }))
@@ -32,60 +103,4 @@ function mapDispatchToProps (dispatch) {
}
}
-inherits(NewAccountModal, Component)
-function NewAccountModal () {
- Component.call(this)
-
- this.state = {
- newAccountName: '',
- }
-}
-
module.exports = connect(mapStateToProps, mapDispatchToProps)(NewAccountModal)
-
-NewAccountModal.prototype.render = function () {
- const { newAccountName } = this.state
-
- return h('div', {}, [
- h('div.new-account-modal-wrapper', {
- }, [
- h('div.new-account-modal-header', {}, [
- 'New Account',
- ]),
-
- h('div.modal-close-x', {
- onClick: this.props.hideModal,
- }),
-
- h('div.new-account-modal-content', {}, [
- 'Account Name',
- ]),
-
- h('div.new-account-input-wrapper', {}, [
- h('input.new-account-input', {
- placeholder: 'E.g. My new account',
- onChange: event => this.setState({ newAccountName: event.target.value }),
- }, []),
- ]),
-
- h('div.new-account-modal-content.after-input', {}, [
- 'or',
- ]),
-
- h('div.new-account-modal-content.after-input.pointer', {
- onClick: () => {
- this.props.hideModal()
- this.props.showImportPage()
- },
- }, 'Import an account'),
-
- h('div.new-account-modal-content.button', {}, [
- h('button.btn-clear', {
- onClick: () => this.props.createAccount(newAccountName),
- }, [
- 'SAVE',
- ]),
- ]),
- ]),
- ])
-}
diff --git a/ui/app/components/modals/shapeshift-deposit-tx-modal.js b/ui/app/components/modals/shapeshift-deposit-tx-modal.js
index 1fd1ade00..24af5a0de 100644
--- a/ui/app/components/modals/shapeshift-deposit-tx-modal.js
+++ b/ui/app/components/modals/shapeshift-deposit-tx-modal.js
@@ -35,6 +35,6 @@ ShapeshiftDepositTxModal.prototype.render = function () {
}, [
h('div', {}, [
h(QrView, {key: 'qr', Qr}),
- ])
+ ]),
])
}
diff --git a/ui/app/components/network.js b/ui/app/components/network.js
index 229d02e36..915818009 100644
--- a/ui/app/components/network.js
+++ b/ui/app/components/network.js
@@ -1,6 +1,6 @@
const Component = require('react').Component
const h = require('react-hyperscript')
-const classnames = require('classnames');
+const classnames = require('classnames')
const inherits = require('util').inherits
const NetworkDropdownIcon = require('./dropdowns/components/network-dropdown-icon')
diff --git a/ui/app/components/pending-tx/confirm-deploy-contract.js b/ui/app/components/pending-tx/confirm-deploy-contract.js
index a0ba94045..ae6c6ef7b 100644
--- a/ui/app/components/pending-tx/confirm-deploy-contract.js
+++ b/ui/app/components/pending-tx/confirm-deploy-contract.js
@@ -10,9 +10,7 @@ const BN = ethUtil.BN
const hexToBn = require('../../../../app/scripts/lib/hex-to-bn')
const { conversionUtil } = require('../../conversion-util')
-const MIN_GAS_PRICE_GWEI_BN = new BN(1)
-const GWEI_FACTOR = new BN(1e9)
-const MIN_GAS_PRICE_BN = MIN_GAS_PRICE_GWEI_BN.mul(GWEI_FACTOR)
+const { MIN_GAS_PRICE_HEX } = require('../send/send-constants')
module.exports = connect(mapStateToProps, mapDispatchToProps)(ConfirmDeployContract)
@@ -166,7 +164,7 @@ ConfirmDeployContract.prototype.getGasFee = function () {
const gasBn = hexToBn(gas)
// Gas Price
- const gasPrice = txParams.gasPrice || MIN_GAS_PRICE_BN.toString(16)
+ const gasPrice = txParams.gasPrice || MIN_GAS_PRICE_HEX
const gasPriceBn = hexToBn(gasPrice)
const txFeeBn = gasBn.mul(gasPriceBn)
diff --git a/ui/app/components/pending-tx/confirm-send-ether.js b/ui/app/components/pending-tx/confirm-send-ether.js
index 2f178f179..d12bc499b 100644
--- a/ui/app/components/pending-tx/confirm-send-ether.js
+++ b/ui/app/components/pending-tx/confirm-send-ether.js
@@ -10,9 +10,7 @@ const BN = ethUtil.BN
const hexToBn = require('../../../../app/scripts/lib/hex-to-bn')
const { conversionUtil, addCurrencies } = require('../../conversion-util')
-const MIN_GAS_PRICE_GWEI_BN = new BN(1)
-const GWEI_FACTOR = new BN(1e9)
-const MIN_GAS_PRICE_BN = MIN_GAS_PRICE_GWEI_BN.mul(GWEI_FACTOR)
+const { MIN_GAS_PRICE_HEX } = require('../send/send-constants')
module.exports = connect(mapStateToProps, mapDispatchToProps)(ConfirmSendEther)
@@ -93,7 +91,7 @@ ConfirmSendEther.prototype.getGasFee = function () {
// const safeGasLimit = safeGasLimitBN.toString(10)
// Gas Price
- const gasPrice = txParams.gasPrice || MIN_GAS_PRICE_BN.toString(16)
+ const gasPrice = txParams.gasPrice || MIN_GAS_PRICE_HEX
const gasPriceBn = hexToBn(gasPrice)
const txFeeBn = gasBn.mul(gasPriceBn)
diff --git a/ui/app/components/pending-tx/confirm-send-token.js b/ui/app/components/pending-tx/confirm-send-token.js
index abb7a0770..3b8ae7f7f 100644
--- a/ui/app/components/pending-tx/confirm-send-token.js
+++ b/ui/app/components/pending-tx/confirm-send-token.js
@@ -10,19 +10,15 @@ const clone = require('clone')
const Identicon = require('../identicon')
const ethUtil = require('ethereumjs-util')
const BN = ethUtil.BN
-const hexToBn = require('../../../../app/scripts/lib/hex-to-bn')
const {
conversionUtil,
multiplyCurrencies,
addCurrencies,
} = require('../../conversion-util')
-const MIN_GAS_PRICE_GWEI_BN = new BN(1)
-const GWEI_FACTOR = new BN(1e9)
-const MIN_GAS_PRICE_BN = MIN_GAS_PRICE_GWEI_BN.mul(GWEI_FACTOR)
+const { MIN_GAS_PRICE_HEX } = require('../send/send-constants')
const {
- getSelectedTokenExchangeRate,
getTokenExchangeRate,
getSelectedAddress,
} = require('../../selectors')
@@ -38,7 +34,6 @@ function mapStateToProps (state, ownProps) {
identities,
currentCurrency,
} = state.metamask
- const accounts = state.metamask.accounts
const selectedAddress = getSelectedAddress(state)
const tokenExchangeRate = getTokenExchangeRate(state, symbol)
@@ -99,7 +94,7 @@ ConfirmSendToken.prototype.getGasFee = function () {
const { decimals } = token
const gas = txParams.gas
- const gasPrice = txParams.gasPrice || MIN_GAS_PRICE_BN.toString(16)
+ const gasPrice = txParams.gasPrice || MIN_GAS_PRICE_HEX
const gasTotal = multiplyCurrencies(gas, gasPrice, {
multiplicandBase: 16,
multiplierBase: 16,
@@ -149,7 +144,7 @@ ConfirmSendToken.prototype.getData = function () {
const { value } = params[0] || {}
const txMeta = this.gatherTxMeta()
const txParams = txMeta.txParams || {}
-
+
return {
from: {
address: txParams.from,
@@ -247,7 +242,6 @@ ConfirmSendToken.prototype.renderTotalPlusGas = function () {
ConfirmSendToken.prototype.render = function () {
const { backToAccountDetail, selectedAddress } = this.props
const txMeta = this.gatherTxMeta()
- const txParams = txMeta.txParams || {}
const {
from: {
diff --git a/ui/app/components/send-token/index.js b/ui/app/components/send-token/index.js
index a95a0a6d8..99d078251 100644
--- a/ui/app/components/send-token/index.js
+++ b/ui/app/components/send-token/index.js
@@ -144,16 +144,15 @@ SendTokenScreen.prototype.validate = function () {
}
SendTokenScreen.prototype.setErrorsFor = function (field) {
- const { balance, selectedToken } = this.props
const { errors: previousErrors } = this.state
const {
isValid,
- errors: newErrors
+ errors: newErrors,
} = this.validate()
const nextErrors = Object.assign({}, previousErrors, {
- [field]: newErrors[field] || null
+ [field]: newErrors[field] || null,
})
if (!isValid) {
@@ -167,7 +166,7 @@ SendTokenScreen.prototype.setErrorsFor = function (field) {
SendTokenScreen.prototype.clearErrorsFor = function (field) {
const { errors: previousErrors } = this.state
const nextErrors = Object.assign({}, previousErrors, {
- [field]: null
+ [field]: null,
})
this.setState({
@@ -429,7 +428,7 @@ SendTokenScreen.prototype.render = function () {
this.renderAmountInput(),
this.renderGasInput(),
this.renderMemoInput(),
- warning && h('div.send-screen-input-wrapper--error', {},
+ warning && h('div.send-screen-input-wrapper--error', {},
h('div.send-screen-input-wrapper__error-message', [
warning,
])
diff --git a/ui/app/components/send/account-list-item.js b/ui/app/components/send/account-list-item.js
index cc514cbd4..2378a4671 100644
--- a/ui/app/components/send/account-list-item.js
+++ b/ui/app/components/send/account-list-item.js
@@ -68,4 +68,4 @@ AccountListItem.prototype.render = function () {
}, name),
])
-} \ No newline at end of file
+}
diff --git a/ui/app/components/send/currency-display.js b/ui/app/components/send/currency-display.js
index 5dba6a8dd..45986e371 100644
--- a/ui/app/components/send/currency-display.js
+++ b/ui/app/components/send/currency-display.js
@@ -1,7 +1,6 @@
const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
-const Identicon = require('../identicon')
const { conversionUtil, multiplyCurrencies } = require('../../conversion-util')
module.exports = CurrencyDisplay
@@ -48,8 +47,6 @@ CurrencyDisplay.prototype.render = function () {
conversionRate,
primaryCurrency,
convertedCurrency,
- convertedPrefix = '',
- placeholder = '0',
readOnly = false,
inError = false,
value: initValue,
@@ -74,7 +71,7 @@ CurrencyDisplay.prototype.render = function () {
conversionRate,
})
- const inputSizeMultiplier = readOnly ? 1 : 1.2;
+ const inputSizeMultiplier = readOnly ? 1 : 1.2
return h('div', {
className,
@@ -98,15 +95,13 @@ CurrencyDisplay.prototype.render = function () {
if (newValue === '') {
newValue = '0'
- }
- else if (newValue.match(/^0[1-9]$/)) {
+ } else if (newValue.match(/^0[1-9]$/)) {
newValue = newValue.match(/[1-9]/)[0]
}
if (newValue && !isValidInput(newValue)) {
event.preventDefault()
- }
- else {
+ } else {
validate(this.getAmount(newValue))
this.setState({ value: newValue })
}
@@ -125,6 +120,6 @@ CurrencyDisplay.prototype.render = function () {
}, `${convertedValue} ${convertedCurrency.toUpperCase()}`),
])
-
+
}
diff --git a/ui/app/components/send/eth-fee-display.js b/ui/app/components/send/eth-fee-display.js
index 8b4cec16c..9eda5ec62 100644
--- a/ui/app/components/send/eth-fee-display.js
+++ b/ui/app/components/send/eth-fee-display.js
@@ -30,8 +30,8 @@ EthFeeDisplay.prototype.render = function () {
color: '#5d5d5d',
fontSize: '16px',
fontFamily: 'DIN OT',
- lineHeight: '22.4px'
- }
+ lineHeight: '22.4px',
+ },
})
}
diff --git a/ui/app/components/send/from-dropdown.js b/ui/app/components/send/from-dropdown.js
index 6f2b9da68..bcae5ede8 100644
--- a/ui/app/components/send/from-dropdown.js
+++ b/ui/app/components/send/from-dropdown.js
@@ -1,7 +1,6 @@
const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
-const Identicon = require('../identicon')
const AccountListItem = require('./account-list-item')
module.exports = FromDropdown
@@ -36,13 +35,13 @@ FromDropdown.prototype.renderDropdown = function () {
h('div.send-v2__from-dropdown__list', {}, [
...accounts.map(account => h(AccountListItem, {
- account,
+ account,
handleClick: () => {
onSelect(account)
closeDropdown()
- },
+ },
icon: this.getListItemIcon(account, selectedAccount),
- }))
+ })),
]),
@@ -51,10 +50,8 @@ FromDropdown.prototype.renderDropdown = function () {
FromDropdown.prototype.render = function () {
const {
- accounts,
selectedAccount,
openDropdown,
- closeDropdown,
dropdownOpen,
} = this.props
@@ -63,12 +60,12 @@ FromDropdown.prototype.render = function () {
h(AccountListItem, {
account: selectedAccount,
handleClick: openDropdown,
- icon: h(`i.fa.fa-caret-down.fa-lg`, { style: { color: '#dedede' } })
+ icon: h(`i.fa.fa-caret-down.fa-lg`, { style: { color: '#dedede' } }),
}),
dropdownOpen && this.renderDropdown(),
])
-
+
}
diff --git a/ui/app/components/send/gas-fee-display-v2.js b/ui/app/components/send/gas-fee-display-v2.js
index 0e23b63ac..806c33f0a 100644
--- a/ui/app/components/send/gas-fee-display-v2.js
+++ b/ui/app/components/send/gas-fee-display-v2.js
@@ -23,21 +23,20 @@ GasFeeDisplay.prototype.render = function () {
gasTotal
? h(CurrencyDisplay, {
- primaryCurrency: 'ETH',
+ primaryCurrency,
convertedCurrency,
value: gasTotal,
conversionRate,
convertedPrefix: '$',
readOnly: true,
})
- : h('div.currency-display', 'Loading...')
- ,
+ : h('div.currency-display', 'Loading...'),
h('div.send-v2__sliders-icon-container', {
onClick,
}, [
h('i.fa.fa-sliders.send-v2__sliders-icon'),
- ])
+ ]),
])
}
diff --git a/ui/app/components/send/memo-textarea.js b/ui/app/components/send/memo-textarea.js
index 4005b9493..f4bb24bf8 100644
--- a/ui/app/components/send/memo-textarea.js
+++ b/ui/app/components/send/memo-textarea.js
@@ -1,33 +1,33 @@
-const Component = require('react').Component
-const h = require('react-hyperscript')
-const inherits = require('util').inherits
-const Identicon = require('../identicon')
-
-module.exports = MemoTextArea
-
-inherits(MemoTextArea, Component)
-function MemoTextArea () {
- Component.call(this)
-}
-
-MemoTextArea.prototype.render = function () {
- const { memo, identities, onChange } = this.props
-
- return h('div.send-v2__memo-text-area', [
-
- h('textarea.send-v2__memo-text-area__input', {
- placeholder: 'Optional',
- value: memo,
- onChange,
- // onBlur: () => {
- // this.setErrorsFor('memo')
- // },
- onFocus: event => {
- // this.clearErrorsFor('memo')
- },
- }),
-
- ])
-
-}
+// const Component = require('react').Component
+// const h = require('react-hyperscript')
+// const inherits = require('util').inherits
+// const Identicon = require('../identicon')
+
+// module.exports = MemoTextArea
+
+// inherits(MemoTextArea, Component)
+// function MemoTextArea () {
+// Component.call(this)
+// }
+
+// MemoTextArea.prototype.render = function () {
+// const { memo, identities, onChange } = this.props
+
+// return h('div.send-v2__memo-text-area', [
+
+// h('textarea.send-v2__memo-text-area__input', {
+// placeholder: 'Optional',
+// value: memo,
+// onChange,
+// // onBlur: () => {
+// // this.setErrorsFor('memo')
+// // },
+// onFocus: event => {
+// // this.clearErrorsFor('memo')
+// },
+// }),
+
+// ])
+
+// }
diff --git a/ui/app/components/send/send-constants.js b/ui/app/components/send/send-constants.js
index 8b56607cc..0a4f85bc9 100644
--- a/ui/app/components/send/send-constants.js
+++ b/ui/app/components/send/send-constants.js
@@ -1,20 +1,18 @@
-const Identicon = require('../identicon')
-const { multiplyCurrencies } = require('../../conversion-util')
+const ethUtil = require('ethereumjs-util')
+const { conversionUtil, multiplyCurrencies } = require('../../conversion-util')
-const MIN_GAS_PRICE_GWEI = '1'
-const GWEI_FACTOR = '1e9'
-const MIN_GAS_PRICE_HEX = multiplyCurrencies(GWEI_FACTOR, MIN_GAS_PRICE_GWEI, {
- multiplicandBase: 16,
- multiplierBase: 16,
- toNumericBase: 'hex',
-})
-const MIN_GAS_PRICE_DEC = multiplyCurrencies(GWEI_FACTOR, MIN_GAS_PRICE_GWEI, {
- multiplicandBase: 16,
- multiplierBase: 16,
- toNumericBase: 'dec',
-})
+const MIN_GAS_PRICE_HEX = (100000000).toString(16)
+const MIN_GAS_PRICE_DEC = '100000000'
const MIN_GAS_LIMIT_HEX = (21000).toString(16)
const MIN_GAS_LIMIT_DEC = 21000
+
+const MIN_GAS_PRICE_GWEI = ethUtil.addHexPrefix(conversionUtil(MIN_GAS_PRICE_HEX, {
+ fromDenomination: 'WEI',
+ toDenomination: 'GWEI',
+ fromNumericBase: 'hex',
+ toNumericBase: 'hex',
+}))
+
const MIN_GAS_TOTAL = multiplyCurrencies(MIN_GAS_LIMIT_HEX, MIN_GAS_PRICE_HEX, {
toNumericBase: 'hex',
multiplicandBase: 16,
diff --git a/ui/app/components/send/send-utils.js b/ui/app/components/send/send-utils.js
index bf096d610..6ec04a223 100644
--- a/ui/app/components/send/send-utils.js
+++ b/ui/app/components/send/send-utils.js
@@ -1,6 +1,6 @@
const { addCurrencies, conversionGreaterThan } = require('../../conversion-util')
-function isBalanceSufficient({
+function isBalanceSufficient ({
amount,
gasTotal,
balance,
@@ -27,7 +27,6 @@ function isBalanceSufficient({
fromNumericBase: 'hex',
conversionRate: amountConversionRate,
fromCurrency: selectedToken || primaryCurrency,
- conversionRate: amountConversionRate,
},
)
@@ -36,4 +35,4 @@ function isBalanceSufficient({
module.exports = {
isBalanceSufficient,
-} \ No newline at end of file
+}
diff --git a/ui/app/components/send/send-v2-container.js b/ui/app/components/send/send-v2-container.js
index fb2634de2..5a6e83ae6 100644
--- a/ui/app/components/send/send-v2-container.js
+++ b/ui/app/components/send/send-v2-container.js
@@ -3,17 +3,12 @@ const actions = require('../../actions')
const abi = require('ethereumjs-abi')
const SendEther = require('../../send-v2')
-const { multiplyCurrencies } = require('../../conversion-util')
-
const {
accountsWithSendEtherInfoSelector,
getCurrentAccountWithSendEtherInfo,
conversionRateSelector,
getSelectedToken,
- getSelectedTokenExchangeRate,
getSelectedAddress,
- getGasPrice,
- getGasLimit,
getAddressBook,
getSendFrom,
getCurrentCurrency,
@@ -26,12 +21,11 @@ function mapStateToProps (state) {
const fromAccounts = accountsWithSendEtherInfoSelector(state)
const selectedAddress = getSelectedAddress(state)
const selectedToken = getSelectedToken(state)
- const tokenExchangeRates = state.metamask.tokenExchangeRates
const conversionRate = conversionRateSelector(state)
- let data;
- let primaryCurrency;
- let tokenToFiatRate;
+ let data
+ let primaryCurrency
+ let tokenToFiatRate
if (selectedToken) {
data = Array.prototype.map.call(
abi.rawEncode(['address', 'uint256'], [selectedAddress, '0x0']),
@@ -76,6 +70,6 @@ function mapDispatchToProps (dispatch) {
updateSendMemo: newMemo => dispatch(actions.updateSendMemo(newMemo)),
updateSendErrors: newError => dispatch(actions.updateSendErrors(newError)),
goHome: () => dispatch(actions.goHome()),
- clearSend: () => dispatch(actions.clearSend())
+ clearSend: () => dispatch(actions.clearSend()),
}
}
diff --git a/ui/app/components/send/to-autocomplete.js b/ui/app/components/send/to-autocomplete.js
index ab490155b..df43fcc4a 100644
--- a/ui/app/components/send/to-autocomplete.js
+++ b/ui/app/components/send/to-autocomplete.js
@@ -1,7 +1,6 @@
const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
-const Identicon = require('../identicon')
const AccountListItem = require('./account-list-item')
module.exports = ToAutoComplete
@@ -23,7 +22,6 @@ ToAutoComplete.prototype.getListItemIcon = function (listItemAddress, toAddress)
ToAutoComplete.prototype.renderDropdown = function () {
const {
- accounts,
closeDropdown,
onChange,
to,
@@ -39,15 +37,15 @@ ToAutoComplete.prototype.renderDropdown = function () {
h('div.send-v2__from-dropdown__list', {}, [
...accountsToRender.map(account => h(AccountListItem, {
- account,
+ account,
handleClick: () => {
onChange(account.address)
closeDropdown()
- },
+ },
icon: this.getListItemIcon(account.address, to),
displayBalance: false,
displayAddress: true,
- }))
+ })),
]),
@@ -69,8 +67,7 @@ ToAutoComplete.prototype.handleInputEvent = function (event = {}, cb) {
this.setState({ accountsToRender: [] })
event.target && event.target.select()
closeDropdown()
- }
- else {
+ } else {
this.setState({ accountsToRender: matchingAccounts })
openDropdown()
}
@@ -86,9 +83,6 @@ ToAutoComplete.prototype.componentDidUpdate = function (nextProps, nextState) {
ToAutoComplete.prototype.render = function () {
const {
to,
- accounts,
- openDropdown,
- closeDropdown,
dropdownOpen,
onChange,
inError,
@@ -98,13 +92,13 @@ ToAutoComplete.prototype.render = function () {
h('input.send-v2__to-autocomplete__input', {
placeholder: 'Recipient Address',
- className: inError ? `send-v2__error-border` : '',
+ className: inError ? `send-v2__error-border` : '',
value: to,
onChange: event => onChange(event.target.value),
onFocus: event => this.handleInputEvent(event),
style: {
borderColor: inError ? 'red' : null,
- }
+ },
}),
!to && h(`i.fa.fa-caret-down.fa-lg.send-v2__to-autocomplete__down-caret`, {
diff --git a/ui/app/components/send/usd-fee-display.js b/ui/app/components/send/usd-fee-display.js
index 6ee38f1b5..4cf31a493 100644
--- a/ui/app/components/send/usd-fee-display.js
+++ b/ui/app/components/send/usd-fee-display.js
@@ -28,8 +28,8 @@ USDFeeDisplay.prototype.render = function () {
color: '#5d5d5d',
fontSize: '16px',
fontFamily: 'DIN OT',
- lineHeight: '22.4px'
- }
+ lineHeight: '22.4px',
+ },
})
}
diff --git a/ui/app/components/signature-request.js b/ui/app/components/signature-request.js
index a0ecbe8ec..c5cc23aa8 100644
--- a/ui/app/components/signature-request.js
+++ b/ui/app/components/signature-request.js
@@ -4,9 +4,9 @@ const inherits = require('util').inherits
const Identicon = require('./identicon')
const connect = require('react-redux').connect
const ethUtil = require('ethereumjs-util')
-const PendingTxDetails = require('./pending-personal-msg-details')
+const classnames = require('classnames')
+
const AccountDropdownMini = require('./dropdowns/account-dropdown-mini')
-const BinaryRenderer = require('./binary-renderer')
const actions = require('../actions')
const { conversionUtil } = require('../conversion-util')
@@ -27,13 +27,13 @@ function mapStateToProps (state) {
requester: null,
requesterAddress: null,
accounts: accountsWithSendEtherInfoSelector(state),
- conversionRate: conversionRateSelector(state)
+ conversionRate: conversionRateSelector(state),
}
}
function mapDispatchToProps (dispatch) {
return {
- goHome: () => dispatch(actions.goHome())
+ goHome: () => dispatch(actions.goHome()),
}
}
@@ -84,7 +84,7 @@ SignatureRequest.prototype.renderAccountDropdown = function () {
dropdownOpen: accountDropdownOpen,
openDropdown: () => this.setState({ accountDropdownOpen: true }),
closeDropdown: () => this.setState({ accountDropdownOpen: false }),
- })
+ }),
])
}
@@ -113,7 +113,7 @@ SignatureRequest.prototype.renderAccountInfo = function () {
return h('div.request-signature__account-info', [
this.renderAccountDropdown(),
-
+
this.renderRequestIcon(),
this.renderBalance(),
@@ -128,18 +128,16 @@ SignatureRequest.prototype.renderRequestIcon = function () {
h(Identicon, {
diameter: 40,
address: requesterAddress,
- })
+ }),
])
}
SignatureRequest.prototype.renderRequestInfo = function () {
- const { requester } = this.props
-
return h('div.request-signature__request-info', [
h('div.request-signature__headline', [
`Your signature is being requested`,
- ])
+ ]),
])
}
@@ -163,11 +161,9 @@ SignatureRequest.prototype.renderBody = function () {
if (type === 'personal_sign') {
rows = [{ name: 'Message', value: this.msgHexToText(data) }]
- }
- else if (type === 'eth_signTypedData') {
+ } else if (type === 'eth_signTypedData') {
rows = data
- }
- else if (type === 'eth_sign') {
+ } else if (type === 'eth_sign') {
rows = [{ name: 'Message', value: data }]
notice = `Signing this message can have
dangerous side effects. Only sign messages from
@@ -181,13 +177,18 @@ SignatureRequest.prototype.renderBody = function () {
this.renderRequestInfo(),
- h('div.request-signature__notice', [notice]),
+ h('div.request-signature__notice', {
+ className: classnames({
+ 'request-signature__notice': type === 'personal_sign' || type === 'eth_signTypedData',
+ 'request-signature__warning': type === 'eth_sign',
+ }),
+ }, [notice]),
h('div.request-signature__rows', [
...rows.map(({ name, value }) => {
return h('div.request-signature__row', [
- h('div.request-signature__row-title', [`${name}:`]),
+ h('div.request-signature__row-title', [`${name}:`]),
h('div.request-signature__row-value', value),
])
}),
@@ -199,7 +200,6 @@ SignatureRequest.prototype.renderBody = function () {
SignatureRequest.prototype.renderFooter = function () {
const {
- goHome,
signPersonalMessage,
signTypedMessage,
cancelPersonalMessage,
@@ -216,12 +216,10 @@ SignatureRequest.prototype.renderFooter = function () {
if (type === 'personal_sign') {
cancel = cancelPersonalMessage
sign = signPersonalMessage
- }
- else if (type === 'eth_signTypedData') {
+ } else if (type === 'eth_signTypedData') {
cancel = cancelTypedMessage
sign = signTypedMessage
- }
- else if (type === 'eth_sign') {
+ } else if (type === 'eth_sign') {
cancel = cancelMessage
sign = signMessage
}
diff --git a/ui/app/components/tab-bar.js b/ui/app/components/tab-bar.js
index fe4076ed0..0edced119 100644
--- a/ui/app/components/tab-bar.js
+++ b/ui/app/components/tab-bar.js
@@ -1,5 +1,6 @@
const { Component } = require('react')
const h = require('react-hyperscript')
+const PropTypes = require('react').PropTypes
const classnames = require('classnames')
class TabBar extends Component {
@@ -37,4 +38,10 @@ class TabBar extends Component {
}
}
+TabBar.propTypes = {
+ defaultTab: PropTypes.string,
+ tabs: PropTypes.array,
+ tabSelected: PropTypes.func,
+}
+
module.exports = TabBar
diff --git a/ui/app/components/token-cell.js b/ui/app/components/token-cell.js
index 6bb42204e..b40c0ec0d 100644
--- a/ui/app/components/token-cell.js
+++ b/ui/app/components/token-cell.js
@@ -6,7 +6,7 @@ const Identicon = require('./identicon')
const prefixForNetwork = require('../../lib/etherscan-prefix-for-network')
const selectors = require('../selectors')
const actions = require('../actions')
-const { conversionUtil } = require('../conversion-util')
+const { conversionUtil, multiplyCurrencies } = require('../conversion-util')
const TokenMenuDropdown = require('./dropdowns/token-menu-dropdown.js')
@@ -17,7 +17,7 @@ function mapStateToProps (state) {
selectedTokenAddress: state.metamask.selectedTokenAddress,
userAddress: selectors.getSelectedAddress(state),
tokenExchangeRates: state.metamask.tokenExchangeRates,
- ethToUSDRate: state.metamask.conversionRate,
+ conversionRate: state.metamask.conversionRate,
sidebarOpen: state.appState.sidebarOpen,
}
}
@@ -61,32 +61,36 @@ TokenCell.prototype.render = function () {
setSelectedToken,
selectedTokenAddress,
tokenExchangeRates,
- ethToUSDRate,
+ conversionRate,
hideSidebar,
sidebarOpen,
currentCurrency,
// userAddress,
} = props
-
- const pair = `${symbol.toLowerCase()}_eth`;
- let currentTokenToEthRate;
- let currentTokenInFiat;
- let formattedUSD = ''
+ const pair = `${symbol.toLowerCase()}_eth`
+
+ let currentTokenToFiatRate
+ let currentTokenInFiat
+ let formattedFiat = ''
if (tokenExchangeRates[pair]) {
- currentTokenToEthRate = tokenExchangeRates[pair].rate;
+ currentTokenToFiatRate = multiplyCurrencies(
+ tokenExchangeRates[pair].rate,
+ conversionRate
+ )
currentTokenInFiat = conversionUtil(string, {
fromNumericBase: 'dec',
fromCurrency: symbol,
- toCurrency: 'USD',
+ toCurrency: currentCurrency.toUpperCase(),
numberOfDecimals: 2,
- conversionRate: currentTokenToEthRate,
- ethToUSDRate,
+ conversionRate: currentTokenToFiatRate,
})
- formattedUSD = `${currentTokenInFiat} ${currentCurrency.toUpperCase()}`;
+ formattedFiat = `${currentTokenInFiat} ${currentCurrency.toUpperCase()}`
}
-
+
+ const showFiat = Boolean(currentTokenInFiat) && currentCurrency.toUpperCase() !== symbol
+
return (
h('div.token-list-item', {
className: `token-list-item ${selectedTokenAddress === address ? 'token-list-item--active' : ''}`,
@@ -108,9 +112,9 @@ TokenCell.prototype.render = function () {
h('h.token-list-item__balance-wrapper', null, [
h('h3.token-list-item__token-balance', `${string || 0} ${symbol}`),
- h('div.token-list-item__fiat-amount', {
+ showFiat && h('div.token-list-item__fiat-amount', {
style: {},
- }, formattedUSD),
+ }, formattedFiat),
]),
h('i.fa.fa-ellipsis-h.fa-lg.token-list-item__ellipsis.cursor-pointer', {
diff --git a/ui/app/components/token-list.js b/ui/app/components/token-list.js
index 4959f1cd5..b6a27fd5a 100644
--- a/ui/app/components/token-list.js
+++ b/ui/app/components/token-list.js
@@ -3,7 +3,6 @@ const h = require('react-hyperscript')
const inherits = require('util').inherits
const TokenTracker = require('eth-token-tracker')
const TokenCell = require('./token-cell.js')
-const normalizeAddress = require('eth-sig-util').normalize
const connect = require('react-redux').connect
const selectors = require('../selectors')
@@ -38,6 +37,7 @@ function TokenList () {
}
TokenList.prototype.render = function () {
+ const { userAddress } = this.props
const state = this.state
const { tokens, isLoading, error } = state
@@ -162,15 +162,15 @@ TokenList.prototype.componentWillUnmount = function () {
this.tracker.stop()
}
-function uniqueMergeTokens (tokensA, tokensB = []) {
- const uniqueAddresses = []
- const result = []
- tokensA.concat(tokensB).forEach((token) => {
- const normal = normalizeAddress(token.address)
- if (!uniqueAddresses.includes(normal)) {
- uniqueAddresses.push(normal)
- result.push(token)
- }
- })
- return result
-}
+// function uniqueMergeTokens (tokensA, tokensB = []) {
+// const uniqueAddresses = []
+// const result = []
+// tokensA.concat(tokensB).forEach((token) => {
+// const normal = normalizeAddress(token.address)
+// if (!uniqueAddresses.includes(normal)) {
+// uniqueAddresses.push(normal)
+// result.push(token)
+// }
+// })
+// return result
+// }
diff --git a/ui/app/components/transaction-list-item.js b/ui/app/components/transaction-list-item.js
index 21f2b8236..255f0e5eb 100644
--- a/ui/app/components/transaction-list-item.js
+++ b/ui/app/components/transaction-list-item.js
@@ -142,7 +142,7 @@ function formatDate (date) {
}
function renderErrorOrWarning (transaction) {
- const { status, err, warning } = transaction
+ const { status } = transaction
// show rejected
if (status === 'rejected') {
@@ -151,31 +151,31 @@ function renderErrorOrWarning (transaction) {
if (transaction.err || transaction.warning) {
const { err, warning = {} } = transaction
const errFirst = !!((err && warning) || err)
- const message = errFirst ? err.message : warning.message
errFirst ? err.message : warning.message
- // show error
- if (err) {
- const message = err.message || ''
- return (
- h(Tooltip, {
- title: message,
- position: 'bottom',
- }, [
- h(`span.error`, ` (Failed)`),
- ])
- )
- }
-
- // show warning
- if (warning) {
- const message = warning.message
- return h(Tooltip, {
- title: message,
- position: 'bottom',
- }, [
- h(`span.warning`, ` (Warning)`),
- ])
+ // show error
+ if (err) {
+ const message = err.message || ''
+ return (
+ h(Tooltip, {
+ title: message,
+ position: 'bottom',
+ }, [
+ h(`span.error`, ` (Failed)`),
+ ])
+ )
+ }
+
+ // show warning
+ if (warning) {
+ const message = warning.message
+ return h(Tooltip, {
+ title: message,
+ position: 'bottom',
+ }, [
+ h(`span.warning`, ` (Warning)`),
+ ])
+ }
}
}
diff --git a/ui/app/components/tx-list-item.js b/ui/app/components/tx-list-item.js
index 3bb9a2eda..26de19f15 100644
--- a/ui/app/components/tx-list-item.js
+++ b/ui/app/components/tx-list-item.js
@@ -6,10 +6,10 @@ const classnames = require('classnames')
const abi = require('human-standard-token-abi')
const abiDecoder = require('abi-decoder')
abiDecoder.addABI(abi)
-const prefixForNetwork = require('../../lib/etherscan-prefix-for-network')
const Identicon = require('./identicon')
+const contractMap = require('eth-contract-metadata')
-const { conversionUtil } = require('../conversion-util')
+const { conversionUtil, multiplyCurrencies } = require('../conversion-util')
const { getCurrentCurrency } = require('../selectors')
@@ -19,12 +19,31 @@ function mapStateToProps (state) {
return {
tokens: state.metamask.tokens,
currentCurrency: getCurrentCurrency(state),
+ tokenExchangeRates: state.metamask.tokenExchangeRates,
}
}
inherits(TxListItem, Component)
function TxListItem () {
Component.call(this)
+
+ this.state = {
+ total: null,
+ fiatTotal: null,
+ }
+}
+
+TxListItem.prototype.componentDidMount = async function () {
+ const { txParams = {} } = this.props
+
+ const decodedData = txParams.data && abiDecoder.decodeMethod(txParams.data)
+ const { name: txDataName } = decodedData || {}
+
+ const { total, fiatTotal } = txDataName === 'transfer'
+ ? await this.getSendTokenTotal()
+ : this.getSendEtherTotal()
+
+ this.setState({ total, fiatTotal })
}
TxListItem.prototype.getAddressText = function () {
@@ -84,23 +103,67 @@ TxListItem.prototype.getSendEtherTotal = function () {
}
}
-TxListItem.prototype.getSendTokenTotal = function () {
+TxListItem.prototype.getTokenInfo = async function () {
+ const { txParams = {}, tokenInfoGetter, tokens } = this.props
+ const toAddress = txParams.to
+
+ let decimals
+ let symbol
+
+ ({ decimals, symbol } = tokens.filter(({ address }) => address === toAddress)[0] || {})
+
+ if (!decimals && !symbol) {
+ ({ decimals, symbol } = contractMap[toAddress] || {})
+ }
+
+ if (!decimals && !symbol) {
+ ({ decimals, symbol } = await tokenInfoGetter(toAddress))
+ }
+
+ return { decimals, symbol }
+}
+
+TxListItem.prototype.getSendTokenTotal = async function () {
const {
txParams = {},
- tokens,
+ conversionRate,
+ tokenExchangeRates,
+ currentCurrency,
} = this.props
- const toAddress = txParams.to
const decodedData = txParams.data && abiDecoder.decodeMethod(txParams.data)
const { params = [] } = decodedData || {}
const { value } = params[1] || {}
- const { decimals, symbol } = tokens.filter(({ address }) => address === toAddress)[0] || {}
-
+ const { decimals, symbol } = await this.getTokenInfo()
const multiplier = Math.pow(10, Number(decimals || 0))
const total = Number(value / multiplier)
+ const pair = symbol && `${symbol.toLowerCase()}_eth`
+
+ let tokenToFiatRate
+ let totalInFiat
+
+ if (tokenExchangeRates[pair]) {
+ tokenToFiatRate = multiplyCurrencies(
+ tokenExchangeRates[pair].rate,
+ conversionRate
+ )
+
+ totalInFiat = conversionUtil(total, {
+ fromNumericBase: 'dec',
+ toNumericBase: 'dec',
+ fromCurrency: symbol,
+ toCurrency: currentCurrency,
+ numberOfDecimals: 2,
+ conversionRate: tokenToFiatRate,
+ })
+ }
+
+ const showFiat = Boolean(totalInFiat) && currentCurrency.toUpperCase() !== symbol
+
return {
total: `${total} ${symbol}`,
+ fiatTotal: showFiat && `${totalInFiat} ${currentCurrency.toUpperCase()}`,
}
}
@@ -112,15 +175,8 @@ TxListItem.prototype.render = function () {
dateString,
address,
className,
- txParams = {},
} = this.props
-
- const decodedData = txParams.data && abiDecoder.decodeMethod(txParams.data)
- const { name: txDataName } = decodedData || {}
-
- const { total, fiatTotal } = txDataName === 'transfer'
- ? this.getSendTokenTotal()
- : this.getSendEtherTotal()
+ const { total, fiatTotal } = this.state
return h(`div${className || ''}`, {
key: transActionId,
@@ -182,10 +238,10 @@ TxListItem.prototype.render = function () {
}),
}, total),
- h('span.tx-list-fiat-value', fiatTotal),
+ fiatTotal && h('span.tx-list-fiat-value', fiatTotal),
]),
]),
- ]) // holding on icon from design
+ ]), // holding on icon from design
])
}
diff --git a/ui/app/components/tx-list.js b/ui/app/components/tx-list.js
index a02849d0e..70722f43e 100644
--- a/ui/app/components/tx-list.js
+++ b/ui/app/components/tx-list.js
@@ -6,9 +6,10 @@ const prefixForNetwork = require('../../lib/etherscan-prefix-for-network')
const selectors = require('../selectors')
const TxListItem = require('./tx-list-item')
const ShiftListItem = require('./shift-list-item')
-const { formatBalance, formatDate } = require('../util')
+const { formatDate } = require('../util')
const { showConfTxPage } = require('../actions')
const classnames = require('classnames')
+const { tokenInfoGetter } = require('../token-util')
module.exports = connect(mapStateToProps, mapDispatchToProps)(TxList)
@@ -21,7 +22,7 @@ function mapStateToProps (state) {
function mapDispatchToProps (dispatch) {
return {
- showConfTxPage: ({ id }) => dispatch(showConfTxPage({ id }))
+ showConfTxPage: ({ id }) => dispatch(showConfTxPage({ id })),
}
}
@@ -30,10 +31,11 @@ function TxList () {
Component.call(this)
}
-TxList.prototype.render = function () {
-
- const { txsToRender, showConfTxPage } = this.props
+TxList.prototype.componentWillMount = function () {
+ this.tokenInfoGetter = tokenInfoGetter()
+}
+TxList.prototype.render = function () {
return h('div.flex-column.tx-list-container', {}, [
h('div.flex-row.tx-list-header-wrapper', [
@@ -93,15 +95,15 @@ TxList.prototype.renderTransactionListItem = function (transaction, conversionRa
txParams: transaction.txParams,
transactionStatus,
transActionId,
- key: transActionId,
dateString,
address,
transactionAmount,
transactionHash,
conversionRate,
+ tokenInfoGetter: this.tokenInfoGetter,
}
- const isUnapproved = transactionStatus === 'unapproved';
+ const isUnapproved = transactionStatus === 'unapproved'
if (isUnapproved) {
opts.onClick = () => showConfTxPage({id: transActionId})
diff --git a/ui/app/conf-tx.js b/ui/app/conf-tx.js
index 97e0646e8..9f273aaec 100644
--- a/ui/app/conf-tx.js
+++ b/ui/app/conf-tx.js
@@ -114,14 +114,14 @@ ConfirmTxScreen.prototype.render = function () {
function currentTxView (opts) {
log.info('rendering current tx view')
const { txData } = opts
- const { txParams, msgParams, type } = txData
+ const { txParams, msgParams } = txData
if (txParams) {
log.debug('txParams detected, rendering pending tx')
return h(PendingTx, opts)
} else if (msgParams) {
log.debug('msgParams detected, rendering pending msg')
-
+
return h(SignatureRequest, opts)
// if (type === 'eth_sign') {
diff --git a/ui/app/conversion-util.js b/ui/app/conversion-util.js
index 2f3fb1678..9359d7c90 100644
--- a/ui/app/conversion-util.js
+++ b/ui/app/conversion-util.js
@@ -13,7 +13,6 @@
* @param {string} [options.fromDenomination = 'WEI'] The denomination of the passed value
* @param {number} [options.numberOfDecimals] The desired number of in the result
* @param {number} [options.conversionRate] The rate to use to make the fromCurrency -> toCurrency conversion
-* @param {number} [options.ethToUSDRate] If present, a second conversion - at ethToUSDRate - happens after conversionRate is applied.
* @returns {(number | string | BN)}
*
* The utility passes value along with the options as a single object to the `converter` function.
@@ -23,6 +22,8 @@
*/
const BigNumber = require('bignumber.js')
+const ethUtil = require('ethereumjs-util')
+const BN = ethUtil.BN
const R = require('ramda')
const { stripHexPrefix } = require('ethereumjs-util')
@@ -38,6 +39,7 @@ const BIG_NUMBER_GWEI_MULTIPLIER = new BigNumber('1000000000')
const convert = R.invoker(1, 'times')
const round = R.invoker(2, 'round')(R.__, BigNumber.ROUND_DOWN)
const invertConversionRate = conversionRate => () => new BigNumber(1.0).div(conversionRate)
+const decToBigNumberViaString = n => R.pipe(String, toBigNumber['dec'])
// Setter Maps
const toBigNumber = {
@@ -51,7 +53,7 @@ const toNormalizedDenomination = {
}
const toSpecifiedDenomination = {
WEI: bigNumber => bigNumber.times(BIG_NUMBER_WEI_MULTIPLIER).round(),
- GWEI: bigNumber => bigNumber.times(BIG_NUMBER_GWEI_MULTIPLIER).round(),
+ GWEI: bigNumber => bigNumber.times(BIG_NUMBER_GWEI_MULTIPLIER).round(1),
}
const baseChange = {
hex: n => n.toString(16),
@@ -95,16 +97,16 @@ const whenPropApplySetterMap = (prop, setterMap) => whenPredSetWithPropAndSetter
// Conversion utility function
const converter = R.pipe(
+ whenPredSetCRWithPropAndSetter(R.prop('conversionRate'), 'conversionRate', decToBigNumberViaString),
whenPredSetCRWithPropAndSetter(R.prop('invertConversionRate'), 'conversionRate', invertConversionRate),
whenPropApplySetterMap('fromNumericBase', toBigNumber),
whenPropApplySetterMap('fromDenomination', toNormalizedDenomination),
whenPredSetWithPropAndSetter(fromAndToCurrencyPropsNotEqual, 'conversionRate', convert),
whenPropApplySetterMap('toDenomination', toSpecifiedDenomination),
- whenPredSetWithPropAndSetter(R.prop('ethToUSDRate'), 'ethToUSDRate', convert),
whenPredSetWithPropAndSetter(R.prop('numberOfDecimals'), 'numberOfDecimals', round),
whenPropApplySetterMap('toNumericBase', baseChange),
R.view(R.lensProp('value'))
-);
+)
const conversionUtil = (value, {
fromCurrency = null,
@@ -115,7 +117,6 @@ const conversionUtil = (value, {
toDenomination,
numberOfDecimals,
conversionRate,
- ethToUSDRate,
invertConversionRate,
}) => converter({
fromCurrency,
@@ -126,18 +127,17 @@ const conversionUtil = (value, {
toDenomination,
numberOfDecimals,
conversionRate,
- ethToUSDRate,
invertConversionRate,
value: value || '0',
-});
+})
const addCurrencies = (a, b, options = {}) => {
const {
aBase,
bBase,
- ...conversionOptions,
+ ...conversionOptions
} = options
- const value = (new BigNumber(a, aBase)).add(b, bBase);
+ const value = (new BigNumber(a, aBase)).add(b, bBase)
return converter({
value,
@@ -149,10 +149,13 @@ const multiplyCurrencies = (a, b, options = {}) => {
const {
multiplicandBase,
multiplierBase,
- ...conversionOptions,
+ ...conversionOptions
} = options
- const value = (new BigNumber(a, multiplicandBase)).times(b, multiplierBase);
+ const bigNumberA = new BigNumber(String(a), multiplicandBase)
+ const bigNumberB = new BigNumber(String(b), multiplierBase)
+
+ const value = bigNumberA.times(bigNumberB)
return converter({
value,
@@ -162,16 +165,41 @@ const multiplyCurrencies = (a, b, options = {}) => {
const conversionGreaterThan = (
{ ...firstProps },
- { ...secondProps },
+ { ...secondProps },
) => {
const firstValue = converter({ ...firstProps })
const secondValue = converter({ ...secondProps })
return firstValue.gt(secondValue)
}
+const conversionGTE = (
+ { ...firstProps },
+ { ...secondProps },
+) => {
+ const firstValue = converter({ ...firstProps })
+ const secondValue = converter({ ...secondProps })
+ return firstValue.greaterThanOrEqualTo(secondValue)
+}
+
+const conversionLTE = (
+ { ...firstProps },
+ { ...secondProps },
+) => {
+ const firstValue = converter({ ...firstProps })
+ const secondValue = converter({ ...secondProps })
+ return firstValue.lessThanOrEqualTo(secondValue)
+}
+
+const toNegative = (n, options = {}) => {
+ return multiplyCurrencies(n, -1, options)
+}
+
module.exports = {
conversionUtil,
addCurrencies,
multiplyCurrencies,
conversionGreaterThan,
-} \ No newline at end of file
+ conversionGTE,
+ conversionLTE,
+ toNegative,
+}
diff --git a/ui/app/css/itcss/components/editable-label.scss b/ui/app/css/itcss/components/editable-label.scss
index 13570610c..c69ea1428 100644
--- a/ui/app/css/itcss/components/editable-label.scss
+++ b/ui/app/css/itcss/components/editable-label.scss
@@ -15,6 +15,7 @@
width: 250px;
font-size: 14px;
text-align: center;
+ border: 1px solid $alto;
&--error {
border: 1px solid $monzo;
diff --git a/ui/app/css/itcss/components/request-signature.scss b/ui/app/css/itcss/components/request-signature.scss
index ee54235d0..d81099bfa 100644
--- a/ui/app/css/itcss/components/request-signature.scss
+++ b/ui/app/css/itcss/components/request-signature.scss
@@ -131,8 +131,8 @@
margin-top: 20px;
}
- &__notice {
- color: #9B9B9B;
+ &__notice,
+ &__warning {
font-family: "Avenir Next";
font-size: 14px;
line-height: 19px;
@@ -142,6 +142,14 @@
width: 100%;
}
+ &__notice {
+ color: $dusty-gray;
+ }
+
+ &__warning {
+ color: $crimson;
+ }
+
&__rows {
height: 100%;
overflow-y: scroll;
diff --git a/ui/app/main-container.js b/ui/app/main-container.js
index 6e2342c2b..031f61e84 100644
--- a/ui/app/main-container.js
+++ b/ui/app/main-container.js
@@ -37,11 +37,7 @@ MainContainer.prototype.render = function () {
break
case 'config':
log.debug('rendering config screen from unlock screen.')
- contents = {
- component: Settings,
- key: 'config',
- }
- break
+ return h(Settings, {key: 'config'})
default:
log.debug('rendering locked screen')
contents = {
diff --git a/ui/app/reducers/app.js b/ui/app/reducers/app.js
index 6fb7f8cca..d84f264c9 100644
--- a/ui/app/reducers/app.js
+++ b/ui/app/reducers/app.js
@@ -44,7 +44,7 @@ function reduceApp (state, action) {
},
previousModalState: {
name: null,
- }
+ },
},
sidebarOpen: false,
networkDropdownOpen: false,
@@ -100,7 +100,7 @@ function reduceApp (state, action) {
state.appState.modal,
{ open: false },
{ modalState: { name: null } },
- { previousModalState: appState.modal.modalState},
+ { previousModalState: appState.modal.modalState},
),
})
diff --git a/ui/app/reducers/metamask.js b/ui/app/reducers/metamask.js
index 7408f827a..50c9712ff 100644
--- a/ui/app/reducers/metamask.js
+++ b/ui/app/reducers/metamask.js
@@ -235,7 +235,7 @@ function reduceMetamask (state, action) {
errors: {
...metamaskState.send.errors,
...action.value,
- }
+ },
},
})
diff --git a/ui/app/send-v2.js b/ui/app/send-v2.js
index e772477ae..6ce3e1b70 100644
--- a/ui/app/send-v2.js
+++ b/ui/app/send-v2.js
@@ -1,8 +1,6 @@
const { inherits } = require('util')
const PersistentForm = require('../lib/persistent-form')
const h = require('react-hyperscript')
-const connect = require('react-redux').connect
-const classnames = require('classnames')
const Identicon = require('./components/identicon')
const FromDropdown = require('./components/send/from-dropdown')
@@ -13,12 +11,9 @@ const GasFeeDisplay = require('./components/send/gas-fee-display-v2')
const { MIN_GAS_TOTAL } = require('./components/send/send-constants')
-const { showModal } = require('./actions')
-
const {
multiplyCurrencies,
conversionGreaterThan,
- addCurrencies,
} = require('./conversion-util')
const {
isBalanceSufficient,
@@ -99,7 +94,7 @@ SendTransactionScreen.prototype.renderHeaderIcon = function () {
diameter: 40,
address: selectedToken.address,
})
- : h('img.send-v2__send-header-icon', { src: '../images/eth_logo.svg' })
+ : h('img.send-v2__send-header-icon', { src: '../images/eth_logo.svg' }),
])
}
@@ -140,12 +135,12 @@ SendTransactionScreen.prototype.renderHeader = function () {
])
}
-SendTransactionScreen.prototype.renderErrorMessage = function(errorType) {
+SendTransactionScreen.prototype.renderErrorMessage = function (errorType) {
const { errors } = this.props
- const errorMessage = errors[errorType];
+ const errorMessage = errors[errorType]
return errorMessage
- ? h('div.send-v2__error', [ errorMessage ] )
+ ? h('div.send-v2__error', [ errorMessage ])
: null
}
@@ -154,7 +149,6 @@ SendTransactionScreen.prototype.renderFromRow = function () {
from,
fromAccounts,
conversionRate,
- setSelectedAddress,
updateSendFrom,
} = this.props
@@ -243,7 +237,6 @@ SendTransactionScreen.prototype.validateAmount = function (value) {
amountConversionRate,
conversionRate,
primaryCurrency,
- toCurrency,
selectedToken,
gasTotal,
} = this.props
@@ -440,7 +433,6 @@ SendTransactionScreen.prototype.onSubmit = function (event) {
signTokenTx,
signTx,
selectedToken,
- toAccounts,
clearSend,
errors: { amount: amountError, to: toError },
} = this.props
diff --git a/ui/app/send.js b/ui/app/send.js
index 5643d927b..517b7690d 100644
--- a/ui/app/send.js
+++ b/ui/app/send.js
@@ -1,547 +1,547 @@
-const { inherits } = require('util')
-const PersistentForm = require('../lib/persistent-form')
-const h = require('react-hyperscript')
-const connect = require('react-redux').connect
-const Identicon = require('./components/identicon')
-const EnsInput = require('./components/ens-input')
-const GasTooltip = require('./components/send/gas-tooltip')
-const CurrencyToggle = require('./components/send/currency-toggle')
-const GasFeeDisplay = require('./components/send/gas-fee-display')
-const { getSelectedIdentity } = require('./selectors')
-
-const {
- showAccountsPage,
- backToAccountDetail,
- displayWarning,
- hideWarning,
- addToAddressBook,
- signTx,
- estimateGas,
- getGasPrice,
-} = require('./actions')
-const { stripHexPrefix, addHexPrefix } = require('ethereumjs-util')
-const { isHex, numericBalance, isValidAddress, allNull } = require('./util')
-const { conversionUtil, conversionGreaterThan } = require('./conversion-util')
-
-module.exports = connect(mapStateToProps)(SendTransactionScreen)
-
-function mapStateToProps (state) {
- const {
- selectedAddress: address,
- accounts,
- identities,
- network,
- addressBook,
- conversionRate,
- currentBlockGasLimit: blockGasLimit,
- } = state.metamask
- const { warning } = state.appState
- const selectedIdentity = getSelectedIdentity(state)
- const account = accounts[address]
-
- return {
- address,
- accounts,
- identities,
- network,
- addressBook,
- conversionRate,
- blockGasLimit,
- warning,
- selectedIdentity,
- error: warning && warning.split('.')[0],
- account,
- identity: identities[address],
- balance: account ? account.balance : null,
- }
-}
-
-inherits(SendTransactionScreen, PersistentForm)
-function SendTransactionScreen () {
- PersistentForm.call(this)
-
- // [WIP] These are the bare minimum of tx props needed to sign a transaction
- // We will need a few more for contract-related interactions
- this.state = {
- newTx: {
- from: '',
- to: '',
- amountToSend: '0x0',
- gasPrice: null,
- gas: null,
- amount: '0x0',
- txData: null,
- memo: '',
- },
- activeCurrency: 'USD',
- tooltipIsOpen: false,
- errors: {},
- isValid: false,
- }
-
- this.back = this.back.bind(this)
- this.closeTooltip = this.closeTooltip.bind(this)
- this.onSubmit = this.onSubmit.bind(this)
- this.setActiveCurrency = this.setActiveCurrency.bind(this)
- this.toggleTooltip = this.toggleTooltip.bind(this)
- this.validate = this.validate.bind(this)
- this.getAmountToSend = this.getAmountToSend.bind(this)
- this.setErrorsFor = this.setErrorsFor.bind(this)
- this.clearErrorsFor = this.clearErrorsFor.bind(this)
-
- this.renderFromInput = this.renderFromInput.bind(this)
- this.renderToInput = this.renderToInput.bind(this)
- this.renderAmountInput = this.renderAmountInput.bind(this)
- this.renderGasInput = this.renderGasInput.bind(this)
- this.renderMemoInput = this.renderMemoInput.bind(this)
- this.renderErrorMessage = this.renderErrorMessage.bind(this)
-}
-
-SendTransactionScreen.prototype.componentWillMount = function () {
- const { newTx } = this.state
- const { address } = this.props
-
- Promise.all([
- this.props.dispatch(getGasPrice()),
- this.props.dispatch(estimateGas({
- from: address,
- gas: '746a528800',
- })),
- ])
- .then(([blockGasPrice, estimatedGas]) => {
- console.log({ blockGasPrice, estimatedGas})
- this.setState({
- newTx: {
- ...newTx,
- gasPrice: blockGasPrice,
- gas: estimatedGas,
- },
- })
- })
-}
-
-SendTransactionScreen.prototype.renderErrorMessage = function(errorType, warning) {
- const { errors } = this.state
- const errorMessage = errors[errorType];
-
- return errorMessage || warning
- ? h('div.send-screen-input-wrapper__error-message', [ errorMessage || warning ])
- : null
-}
-
-SendTransactionScreen.prototype.renderFromInput = function (from, identities) {
- return h('div.send-screen-input-wrapper', [
-
- h('div', 'From:'),
-
- h('input.large-input.send-screen-input', {
- list: 'accounts',
- placeholder: 'Account',
- value: from,
- onChange: (event) => {
- this.setState({
- newTx: {
- ...this.state.newTx,
- from: event.target.value,
- },
- })
- },
- onBlur: () => this.setErrorsFor('from'),
- onFocus: event => {
- this.clearErrorsFor('from')
- this.state.newTx.from && event.target.select()
- },
- }),
-
- h('datalist#accounts', [
- Object.entries(identities).map(([key, { address, name }]) => {
- return h('option', {
- value: address,
- label: name,
- key: address,
- })
- }),
- ]),
-
- this.renderErrorMessage('from'),
-
- ])
-}
-
-SendTransactionScreen.prototype.renderToInput = function (to, identities, addressBook) {
- return h('div.send-screen-input-wrapper', [
-
- h('div', 'To:'),
-
- h('input.large-input.send-screen-input', {
- name: 'address',
- list: 'addresses',
- placeholder: 'Address',
- value: to,
- onChange: (event) => {
- this.setState({
- newTx: {
- ...this.state.newTx,
- to: event.target.value,
- },
- })
- },
- onBlur: () => {
- this.setErrorsFor('to')
- },
- onFocus: event => {
- this.clearErrorsFor('to')
- this.state.newTx.to && event.target.select()
- },
- }),
-
- h('datalist#addresses', [
- // Corresponds to the addresses owned.
- ...Object.entries(identities).map(([key, { address, name }]) => {
- return h('option', {
- value: address,
- label: name,
- key: address,
- })
- }),
- // Corresponds to previously sent-to addresses.
- ...addressBook.map(({ address, name }) => {
- return h('option', {
- value: address,
- label: name,
- key: address,
- })
- }),
- ]),
-
- this.renderErrorMessage('to'),
-
- ])
-}
-
-SendTransactionScreen.prototype.renderAmountInput = function (activeCurrency) {
- return h('div.send-screen-input-wrapper', [
-
- h('div.send-screen-amount-labels', [
- h('span', 'Amount'),
- h(CurrencyToggle, {
- activeCurrency,
- onClick: (newCurrency) => this.setActiveCurrency(newCurrency),
- }), // holding on icon from design
- ]),
-
- h('input.large-input.send-screen-input', {
- placeholder: `0 ${activeCurrency}`,
- type: 'number',
- onChange: (event) => {
- const amountToSend = event.target.value
- ? this.getAmountToSend(event.target.value)
- : '0x0'
-
- this.setState({
- newTx: Object.assign(
- this.state.newTx,
- {
- amount: event.target.value,
- amountToSend: amountToSend,
- }
- ),
- })
- },
- onBlur: () => {
- this.setErrorsFor('amount')
- },
- onFocus: () => this.clearErrorsFor('amount'),
- }),
-
- this.renderErrorMessage('amount'),
-
- ])
-}
-
-SendTransactionScreen.prototype.renderGasInput = function (gasPrice, gas, activeCurrency, conversionRate, blockGasLimit) {
- return h('div.send-screen-input-wrapper', [
- this.state.tooltipIsOpen && h(GasTooltip, {
- className: 'send-tooltip',
- gasPrice,
- gasLimit: gas,
- onClose: this.closeTooltip,
- onFeeChange: ({gasLimit, gasPrice}) => {
- this.setState({
- newTx: {
- ...this.state.newTx,
- gas: gasLimit,
- gasPrice,
- },
- })
- },
- }),
-
- h('div.send-screen-gas-labels', [
- h('span', [
- h('i.fa.fa-bolt'),
- 'Gas fee:',
- ]),
- h('span', 'What\'s this?'),
- ]),
-
- // TODO: handle loading time when switching to USD
- h('div.large-input.send-screen-gas-input', {}, [
- h(GasFeeDisplay, {
- activeCurrency,
- conversionRate,
- gas,
- gasPrice,
- blockGasLimit,
- }),
- h('div.send-screen-gas-input-customize', {
- onClick: this.toggleTooltip,
- }, [
- 'Customize',
- ]),
- ]),
-
- ])
-}
-
-SendTransactionScreen.prototype.renderMemoInput = function () {
- return h('div.send-screen-input-wrapper', [
- h('div', 'Transaction memo (optional)'),
- h('input.large-input.send-screen-input', {
- onChange: () => {
- this.setState({
- newTx: Object.assign(
- this.state.newTx,
- {
- memo: event.target.value,
- }
- ),
- })
- },
- }),
- ])
-}
-
-SendTransactionScreen.prototype.render = function () {
- this.persistentFormParentId = 'send-tx-form'
-
- const props = this.props
- const {
- warning,
- identities,
- addressBook,
- conversionRate,
- } = props
-
- const {
- blockGasLimit,
- newTx,
- activeCurrency,
- isValid,
- } = this.state
- const { gas, gasPrice } = newTx
-
- return (
-
- h('div.send-screen-wrapper', [
- // Main Send token Card
- h('div.send-screen-card', [
-
- h('img.send-eth-icon', { src: '../images/eth_logo.svg' }),
-
- h('div.send-screen__title', 'Send'),
-
- h('div.send-screen__subtitle', 'Send Ethereum to anyone with an Ethereum account'),
-
- this.renderFromInput(this.state.newTx.from, identities),
-
- this.renderToInput(this.state.newTx.to, identities, addressBook),
-
- this.renderAmountInput(activeCurrency),
-
- this.renderGasInput(
- gasPrice || '0x0',
- gas || '0x0',
- activeCurrency,
- conversionRate,
- blockGasLimit
- ),
-
- this.renderMemoInput(),
-
- this.renderErrorMessage(null, warning),
-
- ]),
-
- // Buttons underneath card
- h('section.flex-column.flex-center', [
- h('button.btn-secondary.send-screen__send-button', {
- className: !isValid && 'send-screen__send-button__disabled',
- onClick: (event) => isValid && this.onSubmit(event),
- }, 'Next'),
- h('button.btn-tertiary.send-screen__cancel-button', {
- onClick: this.back,
- }, 'Cancel'),
- ]),
- ])
-
- )
-}
-
-SendTransactionScreen.prototype.toggleTooltip = function () {
- this.setState({ tooltipIsOpen: !this.state.tooltipIsOpen })
-}
-
-SendTransactionScreen.prototype.closeTooltip = function () {
- this.setState({ tooltipIsOpen: false })
-}
-
-SendTransactionScreen.prototype.setActiveCurrency = function (newCurrency) {
- this.setState({ activeCurrency: newCurrency })
-}
-
-SendTransactionScreen.prototype.back = function () {
- var address = this.props.address
- this.props.dispatch(backToAccountDetail(address))
-}
-
-SendTransactionScreen.prototype.validate = function (balance, amountToSend, { to, from }) {
- const sufficientBalance = conversionGreaterThan(
- {
- value: balance,
- fromNumericBase: 'hex',
- },
- {
- value: amountToSend,
- fromNumericBase: 'hex',
- },
- )
-
- const amountLessThanZero = conversionGreaterThan(
- {
- value: 0,
- fromNumericBase: 'dec',
- },
- {
- value: amountToSend,
- fromNumericBase: 'hex',
- },
- )
-
- const errors = {}
-
- if (!sufficientBalance) {
- errors.amount = 'Insufficient funds.'
- }
-
- if (amountLessThanZero) {
- errors.amount = 'Can not send negative amounts of ETH.'
- }
-
- if (!from) {
- errors.from = 'Required'
- }
-
- if (from && !isValidAddress(from)) {
- errors.from = 'Sender address is invalid.'
- }
-
- if (!to) {
- errors.to = 'Required'
- }
-
- if (to && !isValidAddress(to)) {
- errors.to = 'Recipient address is invalid.'
- }
-
- // if (txData && !isHex(stripHexPrefix(txData))) {
- // message = 'Transaction data must be hex string.'
- // return this.props.dispatch(displayWarning(message))
- // }
-
- return {
- isValid: allNull(errors),
- errors,
- }
-}
-
-SendTransactionScreen.prototype.getAmountToSend = function (amount) {
- const { activeCurrency } = this.state
- const { conversionRate } = this.props
-
- return conversionUtil(amount, {
- fromNumericBase: 'dec',
- toNumericBase: 'hex',
- fromCurrency: activeCurrency,
- toCurrency: 'ETH',
- toDenomination: 'WEI',
- conversionRate,
- invertConversionRate: activeCurrency !== 'ETH',
- })
-}
-
-SendTransactionScreen.prototype.setErrorsFor = function (field) {
- const { balance } = this.props
- const { newTx, errors: previousErrors } = this.state
- const { amountToSend } = newTx
-
- const {
- isValid,
- errors: newErrors
- } = this.validate(balance, amountToSend, newTx)
-
- const nextErrors = Object.assign({}, previousErrors, {
- [field]: newErrors[field] || null
- })
-
- if (!isValid) {
- this.setState({
- errors: nextErrors,
- isValid,
- })
- }
-}
-
-SendTransactionScreen.prototype.clearErrorsFor = function (field) {
- const { errors: previousErrors } = this.state
- const nextErrors = Object.assign({}, previousErrors, {
- [field]: null
- })
-
- this.setState({
- errors: nextErrors,
- isValid: allNull(nextErrors),
- })
-}
-
-SendTransactionScreen.prototype.onSubmit = function (event) {
- event.preventDefault()
- const { warning, balance } = this.props
- const state = this.state || {}
-
- const recipient = state.newTx.to
- const sender = state.newTx.from
- const nickname = state.nickname || ' '
-
- // TODO: convert this to hex when created and include it in send
- const txData = state.newTx.memo
-
- this.props.dispatch(hideWarning())
-
- this.props.dispatch(addToAddressBook(recipient, nickname))
-
- var txParams = {
- from: this.state.newTx.from,
- to: this.state.newTx.to,
-
- value: this.state.newTx.amountToSend,
-
- gas: this.state.newTx.gas,
- gasPrice: this.state.newTx.gasPrice,
- }
-
- if (recipient) txParams.to = addHexPrefix(recipient)
- if (txData) txParams.data = txData
-
- this.props.dispatch(signTx(txParams))
-}
+// const { inherits } = require('util')
+// const PersistentForm = require('../lib/persistent-form')
+// const h = require('react-hyperscript')
+// const connect = require('react-redux').connect
+// const Identicon = require('./components/identicon')
+// const EnsInput = require('./components/ens-input')
+// const GasTooltip = require('./components/send/gas-tooltip')
+// const CurrencyToggle = require('./components/send/currency-toggle')
+// const GasFeeDisplay = require('./components/send/gas-fee-display')
+// const { getSelectedIdentity } = require('./selectors')
+
+// const {
+// showAccountsPage,
+// backToAccountDetail,
+// displayWarning,
+// hideWarning,
+// addToAddressBook,
+// signTx,
+// estimateGas,
+// getGasPrice,
+// } = require('./actions')
+// const { stripHexPrefix, addHexPrefix } = require('ethereumjs-util')
+// const { isHex, numericBalance, isValidAddress, allNull } = require('./util')
+// const { conversionUtil, conversionGreaterThan } = require('./conversion-util')
+
+// module.exports = connect(mapStateToProps)(SendTransactionScreen)
+
+// function mapStateToProps (state) {
+// const {
+// selectedAddress: address,
+// accounts,
+// identities,
+// network,
+// addressBook,
+// conversionRate,
+// currentBlockGasLimit: blockGasLimit,
+// } = state.metamask
+// const { warning } = state.appState
+// const selectedIdentity = getSelectedIdentity(state)
+// const account = accounts[address]
+
+// return {
+// address,
+// accounts,
+// identities,
+// network,
+// addressBook,
+// conversionRate,
+// blockGasLimit,
+// warning,
+// selectedIdentity,
+// error: warning && warning.split('.')[0],
+// account,
+// identity: identities[address],
+// balance: account ? account.balance : null,
+// }
+// }
+
+// inherits(SendTransactionScreen, PersistentForm)
+// function SendTransactionScreen () {
+// PersistentForm.call(this)
+
+// // [WIP] These are the bare minimum of tx props needed to sign a transaction
+// // We will need a few more for contract-related interactions
+// this.state = {
+// newTx: {
+// from: '',
+// to: '',
+// amountToSend: '0x0',
+// gasPrice: null,
+// gas: null,
+// amount: '0x0',
+// txData: null,
+// memo: '',
+// },
+// activeCurrency: 'USD',
+// tooltipIsOpen: false,
+// errors: {},
+// isValid: false,
+// }
+
+// this.back = this.back.bind(this)
+// this.closeTooltip = this.closeTooltip.bind(this)
+// this.onSubmit = this.onSubmit.bind(this)
+// this.setActiveCurrency = this.setActiveCurrency.bind(this)
+// this.toggleTooltip = this.toggleTooltip.bind(this)
+// this.validate = this.validate.bind(this)
+// this.getAmountToSend = this.getAmountToSend.bind(this)
+// this.setErrorsFor = this.setErrorsFor.bind(this)
+// this.clearErrorsFor = this.clearErrorsFor.bind(this)
+
+// this.renderFromInput = this.renderFromInput.bind(this)
+// this.renderToInput = this.renderToInput.bind(this)
+// this.renderAmountInput = this.renderAmountInput.bind(this)
+// this.renderGasInput = this.renderGasInput.bind(this)
+// this.renderMemoInput = this.renderMemoInput.bind(this)
+// this.renderErrorMessage = this.renderErrorMessage.bind(this)
+// }
+
+// SendTransactionScreen.prototype.componentWillMount = function () {
+// const { newTx } = this.state
+// const { address } = this.props
+
+// Promise.all([
+// this.props.dispatch(getGasPrice()),
+// this.props.dispatch(estimateGas({
+// from: address,
+// gas: '746a528800',
+// })),
+// ])
+// .then(([blockGasPrice, estimatedGas]) => {
+// console.log({ blockGasPrice, estimatedGas})
+// this.setState({
+// newTx: {
+// ...newTx,
+// gasPrice: blockGasPrice,
+// gas: estimatedGas,
+// },
+// })
+// })
+// }
+
+// SendTransactionScreen.prototype.renderErrorMessage = function(errorType, warning) {
+// const { errors } = this.state
+// const errorMessage = errors[errorType];
+
+// return errorMessage || warning
+// ? h('div.send-screen-input-wrapper__error-message', [ errorMessage || warning ])
+// : null
+// }
+
+// SendTransactionScreen.prototype.renderFromInput = function (from, identities) {
+// return h('div.send-screen-input-wrapper', [
+
+// h('div', 'From:'),
+
+// h('input.large-input.send-screen-input', {
+// list: 'accounts',
+// placeholder: 'Account',
+// value: from,
+// onChange: (event) => {
+// this.setState({
+// newTx: {
+// ...this.state.newTx,
+// from: event.target.value,
+// },
+// })
+// },
+// onBlur: () => this.setErrorsFor('from'),
+// onFocus: event => {
+// this.clearErrorsFor('from')
+// this.state.newTx.from && event.target.select()
+// },
+// }),
+
+// h('datalist#accounts', [
+// Object.entries(identities).map(([key, { address, name }]) => {
+// return h('option', {
+// value: address,
+// label: name,
+// key: address,
+// })
+// }),
+// ]),
+
+// this.renderErrorMessage('from'),
+
+// ])
+// }
+
+// SendTransactionScreen.prototype.renderToInput = function (to, identities, addressBook) {
+// return h('div.send-screen-input-wrapper', [
+
+// h('div', 'To:'),
+
+// h('input.large-input.send-screen-input', {
+// name: 'address',
+// list: 'addresses',
+// placeholder: 'Address',
+// value: to,
+// onChange: (event) => {
+// this.setState({
+// newTx: {
+// ...this.state.newTx,
+// to: event.target.value,
+// },
+// })
+// },
+// onBlur: () => {
+// this.setErrorsFor('to')
+// },
+// onFocus: event => {
+// this.clearErrorsFor('to')
+// this.state.newTx.to && event.target.select()
+// },
+// }),
+
+// h('datalist#addresses', [
+// // Corresponds to the addresses owned.
+// ...Object.entries(identities).map(([key, { address, name }]) => {
+// return h('option', {
+// value: address,
+// label: name,
+// key: address,
+// })
+// }),
+// // Corresponds to previously sent-to addresses.
+// ...addressBook.map(({ address, name }) => {
+// return h('option', {
+// value: address,
+// label: name,
+// key: address,
+// })
+// }),
+// ]),
+
+// this.renderErrorMessage('to'),
+
+// ])
+// }
+
+// SendTransactionScreen.prototype.renderAmountInput = function (activeCurrency) {
+// return h('div.send-screen-input-wrapper', [
+
+// h('div.send-screen-amount-labels', [
+// h('span', 'Amount'),
+// h(CurrencyToggle, {
+// activeCurrency,
+// onClick: (newCurrency) => this.setActiveCurrency(newCurrency),
+// }), // holding on icon from design
+// ]),
+
+// h('input.large-input.send-screen-input', {
+// placeholder: `0 ${activeCurrency}`,
+// type: 'number',
+// onChange: (event) => {
+// const amountToSend = event.target.value
+// ? this.getAmountToSend(event.target.value)
+// : '0x0'
+
+// this.setState({
+// newTx: Object.assign(
+// this.state.newTx,
+// {
+// amount: event.target.value,
+// amountToSend: amountToSend,
+// }
+// ),
+// })
+// },
+// onBlur: () => {
+// this.setErrorsFor('amount')
+// },
+// onFocus: () => this.clearErrorsFor('amount'),
+// }),
+
+// this.renderErrorMessage('amount'),
+
+// ])
+// }
+
+// SendTransactionScreen.prototype.renderGasInput = function (gasPrice, gas, activeCurrency, conversionRate, blockGasLimit) {
+// return h('div.send-screen-input-wrapper', [
+// this.state.tooltipIsOpen && h(GasTooltip, {
+// className: 'send-tooltip',
+// gasPrice,
+// gasLimit: gas,
+// onClose: this.closeTooltip,
+// onFeeChange: ({gasLimit, gasPrice}) => {
+// this.setState({
+// newTx: {
+// ...this.state.newTx,
+// gas: gasLimit,
+// gasPrice,
+// },
+// })
+// },
+// }),
+
+// h('div.send-screen-gas-labels', [
+// h('span', [
+// h('i.fa.fa-bolt'),
+// 'Gas fee:',
+// ]),
+// h('span', 'What\'s this?'),
+// ]),
+
+// // TODO: handle loading time when switching to USD
+// h('div.large-input.send-screen-gas-input', {}, [
+// h(GasFeeDisplay, {
+// activeCurrency,
+// conversionRate,
+// gas,
+// gasPrice,
+// blockGasLimit,
+// }),
+// h('div.send-screen-gas-input-customize', {
+// onClick: this.toggleTooltip,
+// }, [
+// 'Customize',
+// ]),
+// ]),
+
+// ])
+// }
+
+// SendTransactionScreen.prototype.renderMemoInput = function () {
+// return h('div.send-screen-input-wrapper', [
+// h('div', 'Transaction memo (optional)'),
+// h('input.large-input.send-screen-input', {
+// onChange: () => {
+// this.setState({
+// newTx: Object.assign(
+// this.state.newTx,
+// {
+// memo: event.target.value,
+// }
+// ),
+// })
+// },
+// }),
+// ])
+// }
+
+// SendTransactionScreen.prototype.render = function () {
+// this.persistentFormParentId = 'send-tx-form'
+
+// const props = this.props
+// const {
+// warning,
+// identities,
+// addressBook,
+// conversionRate,
+// } = props
+
+// const {
+// blockGasLimit,
+// newTx,
+// activeCurrency,
+// isValid,
+// } = this.state
+// const { gas, gasPrice } = newTx
+
+// return (
+
+// h('div.send-screen-wrapper', [
+// // Main Send token Card
+// h('div.send-screen-card', [
+
+// h('img.send-eth-icon', { src: '../images/eth_logo.svg' }),
+
+// h('div.send-screen__title', 'Send'),
+
+// h('div.send-screen__subtitle', 'Send Ethereum to anyone with an Ethereum account'),
+
+// this.renderFromInput(this.state.newTx.from, identities),
+
+// this.renderToInput(this.state.newTx.to, identities, addressBook),
+
+// this.renderAmountInput(activeCurrency),
+
+// this.renderGasInput(
+// gasPrice || '0x0',
+// gas || '0x0',
+// activeCurrency,
+// conversionRate,
+// blockGasLimit
+// ),
+
+// this.renderMemoInput(),
+
+// this.renderErrorMessage(null, warning),
+
+// ]),
+
+// // Buttons underneath card
+// h('section.flex-column.flex-center', [
+// h('button.btn-secondary.send-screen__send-button', {
+// className: !isValid && 'send-screen__send-button__disabled',
+// onClick: (event) => isValid && this.onSubmit(event),
+// }, 'Next'),
+// h('button.btn-tertiary.send-screen__cancel-button', {
+// onClick: this.back,
+// }, 'Cancel'),
+// ]),
+// ])
+
+// )
+// }
+
+// SendTransactionScreen.prototype.toggleTooltip = function () {
+// this.setState({ tooltipIsOpen: !this.state.tooltipIsOpen })
+// }
+
+// SendTransactionScreen.prototype.closeTooltip = function () {
+// this.setState({ tooltipIsOpen: false })
+// }
+
+// SendTransactionScreen.prototype.setActiveCurrency = function (newCurrency) {
+// this.setState({ activeCurrency: newCurrency })
+// }
+
+// SendTransactionScreen.prototype.back = function () {
+// var address = this.props.address
+// this.props.dispatch(backToAccountDetail(address))
+// }
+
+// SendTransactionScreen.prototype.validate = function (balance, amountToSend, { to, from }) {
+// const sufficientBalance = conversionGreaterThan(
+// {
+// value: balance,
+// fromNumericBase: 'hex',
+// },
+// {
+// value: amountToSend,
+// fromNumericBase: 'hex',
+// },
+// )
+
+// const amountLessThanZero = conversionGreaterThan(
+// {
+// value: 0,
+// fromNumericBase: 'dec',
+// },
+// {
+// value: amountToSend,
+// fromNumericBase: 'hex',
+// },
+// )
+
+// const errors = {}
+
+// if (!sufficientBalance) {
+// errors.amount = 'Insufficient funds.'
+// }
+
+// if (amountLessThanZero) {
+// errors.amount = 'Can not send negative amounts of ETH.'
+// }
+
+// if (!from) {
+// errors.from = 'Required'
+// }
+
+// if (from && !isValidAddress(from)) {
+// errors.from = 'Sender address is invalid.'
+// }
+
+// if (!to) {
+// errors.to = 'Required'
+// }
+
+// if (to && !isValidAddress(to)) {
+// errors.to = 'Recipient address is invalid.'
+// }
+
+// // if (txData && !isHex(stripHexPrefix(txData))) {
+// // message = 'Transaction data must be hex string.'
+// // return this.props.dispatch(displayWarning(message))
+// // }
+
+// return {
+// isValid: allNull(errors),
+// errors,
+// }
+// }
+
+// SendTransactionScreen.prototype.getAmountToSend = function (amount) {
+// const { activeCurrency } = this.state
+// const { conversionRate } = this.props
+
+// return conversionUtil(amount, {
+// fromNumericBase: 'dec',
+// toNumericBase: 'hex',
+// fromCurrency: activeCurrency,
+// toCurrency: 'ETH',
+// toDenomination: 'WEI',
+// conversionRate,
+// invertConversionRate: activeCurrency !== 'ETH',
+// })
+// }
+
+// SendTransactionScreen.prototype.setErrorsFor = function (field) {
+// const { balance } = this.props
+// const { newTx, errors: previousErrors } = this.state
+// const { amountToSend } = newTx
+
+// const {
+// isValid,
+// errors: newErrors
+// } = this.validate(balance, amountToSend, newTx)
+
+// const nextErrors = Object.assign({}, previousErrors, {
+// [field]: newErrors[field] || null
+// })
+
+// if (!isValid) {
+// this.setState({
+// errors: nextErrors,
+// isValid,
+// })
+// }
+// }
+
+// SendTransactionScreen.prototype.clearErrorsFor = function (field) {
+// const { errors: previousErrors } = this.state
+// const nextErrors = Object.assign({}, previousErrors, {
+// [field]: null
+// })
+
+// this.setState({
+// errors: nextErrors,
+// isValid: allNull(nextErrors),
+// })
+// }
+
+// SendTransactionScreen.prototype.onSubmit = function (event) {
+// event.preventDefault()
+// const { warning, balance } = this.props
+// const state = this.state || {}
+
+// const recipient = state.newTx.to
+// const sender = state.newTx.from
+// const nickname = state.nickname || ' '
+
+// // TODO: convert this to hex when created and include it in send
+// const txData = state.newTx.memo
+
+// this.props.dispatch(hideWarning())
+
+// this.props.dispatch(addToAddressBook(recipient, nickname))
+
+// var txParams = {
+// from: this.state.newTx.from,
+// to: this.state.newTx.to,
+
+// value: this.state.newTx.amountToSend,
+
+// gas: this.state.newTx.gas,
+// gasPrice: this.state.newTx.gasPrice,
+// }
+
+// if (recipient) txParams.to = addHexPrefix(recipient)
+// if (txData) txParams.data = txData
+
+// this.props.dispatch(signTx(txParams))
+// }
diff --git a/ui/app/token-util.js b/ui/app/token-util.js
new file mode 100644
index 000000000..eec518556
--- /dev/null
+++ b/ui/app/token-util.js
@@ -0,0 +1,36 @@
+const abi = require('human-standard-token-abi')
+const Eth = require('ethjs-query')
+const EthContract = require('ethjs-contract')
+
+const tokenInfoGetter = function () {
+ if (typeof global.ethereumProvider === 'undefined') return
+
+ const eth = new Eth(global.ethereumProvider)
+ const contract = new EthContract(eth)
+ const TokenContract = contract(abi)
+
+ const tokens = {}
+
+ return async (address) => {
+ if (tokens[address]) {
+ return tokens[address]
+ }
+
+ const contract = TokenContract.at(address)
+
+ const result = await Promise.all([
+ contract.symbol(),
+ contract.decimals(),
+ ])
+
+ const [ symbol = [], decimals = [] ] = result
+
+ tokens[address] = { symbol: symbol[0], decimals: decimals[0] }
+
+ return tokens[address]
+ }
+}
+
+module.exports = {
+ tokenInfoGetter,
+}