1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-11-26 12:29:06 +01:00

Merge remote-tracking branch 'origin/uat' into mascara-first-screen

This commit is contained in:
frankiebee 2017-11-07 12:29:47 -05:00
commit 08867874cc
64 changed files with 1281 additions and 1146 deletions

View File

@ -46,8 +46,6 @@ class PreferencesController {
} }
removeToken (rawAddress) { removeToken (rawAddress) {
const address = normalizeAddress(rawAddress)
const tokens = this.store.getState().tokens const tokens = this.store.getState().tokens
const updatedTokens = tokens.filter(token => token.address !== rawAddress) const updatedTokens = tokens.filter(token => token.address !== rawAddress)

View File

@ -15,7 +15,7 @@
"test": "npm run lint && npm run test:coverage && npm run test:integration", "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: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: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: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: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", "test:flat": "npm run test:flat:build && karma start test/flat.conf.js",
@ -190,6 +190,7 @@
"enzyme": "^2.8.2", "enzyme": "^2.8.2",
"eslint-plugin-chai": "0.0.1", "eslint-plugin-chai": "0.0.1",
"eslint-plugin-mocha": "^4.9.0", "eslint-plugin-mocha": "^4.9.0",
"eslint-plugin-react": "^7.4.0",
"eth-json-rpc-middleware": "^1.2.7", "eth-json-rpc-middleware": "^1.2.7",
"fs-promise": "^2.0.3", "fs-promise": "^2.0.3",
"gulp": "github:gulpjs/gulp#4.0", "gulp": "github:gulpjs/gulp#4.0",
@ -226,6 +227,7 @@
"react-addons-test-utils": "^15.5.1", "react-addons-test-utils": "^15.5.1",
"react-test-renderer": "^15.5.4", "react-test-renderer": "^15.5.4",
"react-testutils-additions": "^15.2.0", "react-testutils-additions": "^15.2.0",
"redux-test-utils": "^0.1.3",
"sinon": "^4.0.0", "sinon": "^4.0.0",
"stylelint-config-standard": "^17.0.0", "stylelint-config-standard": "^17.0.0",
"tape": "^4.5.1", "tape": "^4.5.1",

View File

@ -40,7 +40,8 @@ async function runFirstTimeUsageTest(assert, done) {
// Scroll through terms // Scroll through terms
const title = app.find('h1').text() 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 // enter password
const pwBox = app.find('#password-box')[0] const pwBox = app.find('#password-box')[0]
@ -66,17 +67,17 @@ async function runFirstTimeUsageTest(assert, done) {
await timeout(1000) 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.') assert.ok(detail, 'Account detail section loaded.')
const sandwich = app.find('.sandwich-expando')[0] await timeout(1000)
sandwich.click()
await timeout() const menu = app.find('.account-menu__icon')[0]
menu.click()
const menu = app.find('.menu-droppo')[0] await timeout(1000)
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') assert.ok(lock, 'Lock menu item found')
lock.click() lock.click()
@ -90,36 +91,30 @@ async function runFirstTimeUsageTest(assert, done) {
await timeout(1000) 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.') assert.ok(detail2, 'Account detail section loaded again.')
await timeout() await timeout()
// open account settings dropdown // open account settings dropdown
const qrButton = app.find('.fa.fa-ellipsis-h')[0] const qrButton = app.find('.wallet-view__details-button')[0]
qrButton.click() qrButton.click()
await timeout(1000) await timeout(1000)
// qr code item const qrHeader = app.find('.editable-label__value')[0]
const qrButton2 = app.find('.dropdown-menu-item')[1] const qrContainer = app.find('.qr-wrapper')[0]
qrButton2.click()
await timeout(1000)
const qrHeader = app.find('.qr-header')[0]
const qrContainer = app.find('#qr-container')[0]
assert.equal(qrHeader.textContent, 'Account 1', 'Should show account label.') assert.equal(qrHeader.textContent, 'Account 1', 'Should show account label.')
assert.ok(qrContainer, 'QR Container found') assert.ok(qrContainer, 'QR Container found')
await timeout() await timeout()
const networkMenu = app.find('.network-indicator')[0] const networkMenu = app.find('.network-component')[0]
networkMenu.click() networkMenu.click()
await timeout() await timeout()
const networkMenu2 = app.find('.network-indicator')[0] const networkMenu2 = app.find('.menu-droppo')[0]
const children2 = networkMenu2.children const children2 = networkMenu2.children
children2.length[3] children2.length[3]
assert.ok(children2, 'All network options present') assert.ok(children2, 'All network options present')

View File

@ -102,14 +102,12 @@ async function runFirstTimeUsageTest (assert, done) {
app.find('.buy-ether__do-it-later').click() app.find('.buy-ether__do-it-later').click()
await timeout(1000) await timeout(1000)
const sandwich = app.find('.sandwich-expando')[0] const menu = app.find('.account-menu__icon')[0]
sandwich.click() menu.click()
await timeout() await timeout()
const menu = app.find('.menu-droppo')[0] const lock = app.find('.account-menu__logout-button')[0]
const children = menu.children
const lock = children[children.length - 2]
assert.ok(lock, 'Lock menu item found') assert.ok(lock, 'Lock menu item found')
lock.click() lock.click()
@ -123,31 +121,25 @@ async function runFirstTimeUsageTest (assert, done) {
await timeout(1000) 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.') assert.ok(detail2, 'Account detail section loaded again.')
await timeout() await timeout()
// open account settings dropdown // open account settings dropdown
const qrButton = app.find('.fa.fa-ellipsis-h')[0] const qrButton = app.find('.wallet-view__details-button')[0]
qrButton.click() qrButton.click()
await timeout(1000) await timeout(1000)
// qr code item const qrHeader = app.find('.editable-label__value')[0]
const qrButton2 = app.find('.dropdown-menu-item')[1] const qrContainer = app.find('.qr-wrapper')[0]
qrButton2.click()
await timeout(1000)
const qrHeader = app.find('.qr-header')[0]
const qrContainer = app.find('#qr-container')[0]
assert.equal(qrHeader.textContent, 'Account 1', 'Should show account label.') assert.equal(qrHeader.textContent, 'Account 1', 'Should show account label.')
assert.ok(qrContainer, 'QR Container found') assert.ok(qrContainer, 'QR Container found')
await timeout() await timeout()
const networkMenu = app.find('.network-indicator')[0] const networkMenu = app.find('.network-component')[0]
networkMenu.click() networkMenu.click()
await timeout() await timeout()
@ -164,4 +156,4 @@ function timeout (time) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
setTimeout(resolve, time || 1500) setTimeout(resolve, time || 1500)
}) })
} }

View File

@ -0,0 +1,11 @@
const shallow = require('enzyme').shallow
module.exports = shallowWithStore
function shallowWithStore (component, store) {
const context = {
store,
}
return shallow(component, { context })
};

View File

@ -1,18 +1,31 @@
var assert = require('assert') const assert = require('assert')
var BalanceComponent = require('../../../ui/app/components/balance-component') 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 () { describe('BalanceComponent', function () {
let balanceComponent let balanceComponent
let store
let component
beforeEach(function () { 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 () { it('shows token balance and convert to fiat value based on conversion rate', function () {
const formattedBalance = '1.23 ETH' const formattedBalance = '1.23 ETH'
const tokenBalance = balanceComponent.getTokenBalance(formattedBalance, false) const tokenBalance = balanceComponent.instance().getTokenBalance(formattedBalance, false)
const fiatDisplayNumber = balanceComponent.getFiatDisplayNumber(formattedBalance, 2) const fiatDisplayNumber = balanceComponent.instance().getFiatDisplayNumber(formattedBalance, 2)
assert.equal('1.23 ETH', tokenBalance) assert.equal('1.23 ETH', tokenBalance)
assert.equal(2.46, fiatDisplayNumber) 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 () { it('shows only the token balance when conversion rate is not available', function () {
const formattedBalance = '1.23 ETH' const formattedBalance = '1.23 ETH'
const tokenBalance = balanceComponent.getTokenBalance(formattedBalance, false) const tokenBalance = balanceComponent.instance().getTokenBalance(formattedBalance, false)
const fiatDisplayNumber = balanceComponent.getFiatDisplayNumber(formattedBalance, 0) const fiatDisplayNumber = balanceComponent.instance().getFiatDisplayNumber(formattedBalance, 0)
assert.equal('1.23 ETH', tokenBalance) assert.equal('1.23 ETH', tokenBalance)
assert.equal('N/A', fiatDisplayNumber) assert.equal('N/A', fiatDisplayNumber)

View File

@ -1,18 +1,22 @@
const assert = require('assert') const assert = require('assert')
const additions = require('react-testutils-additions')
const h = require('react-hyperscript') const h = require('react-hyperscript')
const PendingTx = require('../../../ui/app/components/pending-tx') const PendingTx = require('../../../ui/app/components/pending-tx')
const ReactTestUtils = require('react-addons-test-utils')
const ethUtil = require('ethereumjs-util') const ethUtil = require('ethereumjs-util')
describe('PendingTx', function () { const { createMockStore } = require('redux-test-utils')
const identities = { const shallowWithStore = require('../../lib/shallow-with-store')
'0xfdea65c8e26263f6d9a1b5de9555d2931a33b826': {
name: 'Main Account 1',
balance: '0x00000000000000056bc75e2d63100000',
},
}
const identities = { abc: {}, def: {} }
const mockState = {
metamask: {
accounts: { abc: {} },
identities,
conversionRate: 10,
selectedAddress: 'abc',
}
}
describe('PendingTx', function () {
const gasPrice = '0x4A817C800' // 20 Gwei const gasPrice = '0x4A817C800' // 20 Gwei
const txData = { const txData = {
'id': 5021615666270214, 'id': 5021615666270214,
@ -29,55 +33,35 @@ describe('PendingTx', function () {
'gasLimitSpecified': false, 'gasLimitSpecified': false,
'estimatedGas': '0x5208', '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) { let pendingTxComponent
const renderer = ReactTestUtils.createRenderer() let store
const newGasPrice = '0x77359400' let component
beforeEach(function () {
store = createMockStore(mockState)
component = shallowWithStore(h(PendingTx, props), store)
pendingTxComponent = component
})
const computedBalances = {} it('should render correctly', function (done) {
computedBalances[Object.keys(identities)[0]] = { assert.equal(pendingTxComponent.props().identities, identities)
ethBalance: '0x00000000000000056bc75e2d63100000', done()
}
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 },
},
})
const form = additions.find(component, 'form')[0]
form.checkValidity = () => true
form.getFormEl = () => { return { checkValidity () { return true } } }
ReactTestUtils.Simulate.submit(form, { preventDefault () {}, target: { checkValidity () {
return true
} } })
} catch (e) {
console.log('WHAAAA')
console.error(e)
}
}) })
}) })

View File

@ -12,6 +12,7 @@ const currentNetworkId = 42
const otherNetworkId = 36 const otherNetworkId = 36
const privKey = new Buffer('8718b9618a37d1fc78c436511fc6df3c8258d3250635bba617f33003270ec03e', 'hex') const privKey = new Buffer('8718b9618a37d1fc78c436511fc6df3c8258d3250635bba617f33003270ec03e', 'hex')
describe('PendingTransactionTracker', function () { describe('PendingTransactionTracker', function () {
let pendingTxTracker, txMeta, txMetaNoHash, txMetaNoRawTx, providerResultStub, let pendingTxTracker, txMeta, txMetaNoHash, txMetaNoRawTx, providerResultStub,
provider, txMeta3, txList, knownErrors provider, txMeta3, txList, knownErrors

View File

@ -1,40 +1,45 @@
var assert = require('assert'); const assert = require('assert');
const additions = require('react-testutils-additions');
const h = require('react-hyperscript'); const h = require('react-hyperscript');
const ReactTestUtils = require('react-addons-test-utils');
const sinon = require('sinon'); const sinon = require('sinon');
const path = require('path'); const path = require('path');
const Dropdown = require(path.join(__dirname, '..', '..', '..', '..', 'ui', 'app', 'components', 'dropdown.js')).Dropdown; const Dropdown = require(path.join(__dirname, '..', '..', '..', '..', 'ui', 'app', 'components', 'dropdowns', 'index.js')).Dropdown;
const DropdownMenuItem = require(path.join(__dirname, '..', '..', '..', '..', 'ui', 'app', 'components', 'dropdown.js')).DropdownMenuItem;
const { createMockStore } = require('redux-test-utils')
const shallowWithStore = require('../../../lib/shallow-with-store')
const mockState = {
metamask: {
}
}
describe('Dropdown components', function () { describe('Dropdown components', function () {
let onClickOutside; let onClickOutside;
let closeMenu; let closeMenu;
let onClick; let onClick;
let dropdownComponentProps; let dropdownComponentProps = {
const renderer = ReactTestUtils.createRenderer() isOpen: true,
zIndex: 11,
onClickOutside,
style: {
position: 'absolute',
right: 0,
top: '36px',
},
innerStyle: {},
}
let dropdownComponent
let store
let component
beforeEach(function () { beforeEach(function () {
onClickOutside = sinon.spy(); onClickOutside = sinon.spy();
closeMenu = sinon.spy(); closeMenu = sinon.spy();
onClick = sinon.spy(); onClick = sinon.spy();
dropdownComponentProps = { store = createMockStore(mockState)
isOpen: true, component = shallowWithStore(h(
zIndex: 11,
onClickOutside,
style: {
position: 'absolute',
right: 0,
top: '36px',
},
innerStyle: {},
}
});
it('can render two items', function () {
const dropdownComponent = h(
Dropdown, Dropdown,
dropdownComponentProps, dropdownComponentProps,
[ [
@ -42,74 +47,35 @@ describe('Dropdown components', function () {
.drop-menu-item:hover { background:rgb(235, 235, 235); } .drop-menu-item:hover { background:rgb(235, 235, 235); }
.drop-menu-item i { margin: 11px; } .drop-menu-item i { margin: 11px; }
`), `),
h(DropdownMenuItem, { h('li', {
closeMenu, closeMenu,
onClick, onClick,
}, 'Item 1'), }, 'Item 1'),
h(DropdownMenuItem, { h('li', {
closeMenu, closeMenu,
onClick, onClick,
}, 'Item 2'), }, 'Item 2'),
] ]
) ), store)
dropdownComponent = component.dive()
})
const component = additions.renderIntoDocument(dropdownComponent); it('can render two items', function () {
renderer.render(dropdownComponent); const items = dropdownComponent.find('li');
const items = additions.find(component, 'li');
assert.equal(items.length, 2); assert.equal(items.length, 2);
}); });
it('closes when item clicked', function() { it('closes when item clicked', function() {
const dropdownComponent = h( const items = dropdownComponent.find('li');
Dropdown, const node = items.at(0);
dropdownComponentProps, node.simulate('click');
[ assert.equal(node.props().closeMenu, closeMenu);
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);
}); });
it('invokes click handler when item clicked', function() { it('invokes click handler when item clicked', function() {
const dropdownComponent = h( const items = dropdownComponent.find('li');
Dropdown, const node = items.at(0);
dropdownComponentProps, node.simulate('click');
[
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(onClick.calledOnce, true); assert.equal(onClick.calledOnce, true);
}); });
}); });

View File

@ -17,6 +17,10 @@ function PrivateKeyImportView () {
Component.call(this) Component.call(this)
} }
PrivateKeyImportView.prototype.componentWillUnmount = function () {
this.props.dispatch(actions.displayWarning(null))
}
PrivateKeyImportView.prototype.render = function () { PrivateKeyImportView.prototype.render = function () {
const { error } = this.props const { error } = this.props

View File

@ -224,6 +224,8 @@ var actions = {
TOGGLE_ACCOUNT_MENU: 'TOGGLE_ACCOUNT_MENU', TOGGLE_ACCOUNT_MENU: 'TOGGLE_ACCOUNT_MENU',
toggleAccountMenu, toggleAccountMenu,
useEtherscanProvider,
} }
module.exports = actions module.exports = actions
@ -428,7 +430,7 @@ function addNewAccount () {
forceUpdateMetamaskState(dispatch) forceUpdateMetamaskState(dispatch)
return resolve(newAccountAddress) return resolve(newAccountAddress)
}) })
}); })
} }
} }
@ -619,7 +621,7 @@ function updateSendErrors (error) {
function clearSend () { function clearSend () {
return { 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 () { function lockMetamask () {
log.debug(`background.setLocked`) 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) { function setCurrentAccountTab (newTabName) {
@ -961,10 +1004,10 @@ function addTokens (tokens) {
} }
} }
function updateTokens(newTokens) { function updateTokens (newTokens) {
return { return {
type: actions.UPDATE_TOKENS, type: actions.UPDATE_TOKENS,
newTokens newTokens,
} }
} }
@ -1038,7 +1081,7 @@ function setProviderType (type) {
} }
} }
function updateProviderType(type) { function updateProviderType (type) {
return { return {
type: actions.SET_PROVIDER_TYPE, type: actions.SET_PROVIDER_TYPE,
value: type, value: type,
@ -1196,7 +1239,7 @@ function exportAccount (password, address) {
} }
} }
function exportAccountComplete() { function exportAccountComplete () {
return { return {
type: actions.EXPORT_ACCOUNT, type: actions.EXPORT_ACCOUNT,
} }

View File

@ -21,9 +21,7 @@ const fuse = new Fuse(contractList, {
}) })
const actions = require('./actions') const actions = require('./actions')
const ethUtil = require('ethereumjs-util') const ethUtil = require('ethereumjs-util')
const abi = require('human-standard-token-abi') const { tokenInfoGetter } = require('./token-util')
const Eth = require('ethjs-query')
const EthContract = require('ethjs-contract')
const R = require('ramda') const R = require('ramda')
const emptyAddr = '0x0000000000000000000000000000000000000000' const emptyAddr = '0x0000000000000000000000000000000000000000'
@ -63,11 +61,7 @@ function AddTokenScreen () {
} }
AddTokenScreen.prototype.componentWillMount = function () { AddTokenScreen.prototype.componentWillMount = function () {
if (typeof global.ethereumProvider === 'undefined') return this.tokenInfoGetter = tokenInfoGetter()
this.eth = new Eth(global.ethereumProvider)
this.contract = new EthContract(this.eth)
this.TokenContract = this.contract(abi)
} }
AddTokenScreen.prototype.toggleToken = function (address, token) { AddTokenScreen.prototype.toggleToken = function (address, token) {
@ -164,18 +158,11 @@ AddTokenScreen.prototype.validate = function () {
} }
AddTokenScreen.prototype.attemptToAutoFillTokenParams = async function (address) { AddTokenScreen.prototype.attemptToAutoFillTokenParams = async function (address) {
const contract = this.TokenContract.at(address) const { symbol, decimals } = await this.tokenInfoGetter(address)
const results = await Promise.all([
contract.symbol(),
contract.decimals(),
])
const [ symbol, decimals ] = results
if (symbol && decimals) { if (symbol && decimals) {
this.setState({ this.setState({
customSymbol: symbol[0], customSymbol: symbol,
customDecimals: decimals[0].toString(), customDecimals: decimals.toString(),
}) })
} }
} }

View File

@ -2,7 +2,6 @@ const inherits = require('util').inherits
const Component = require('react').Component const Component = require('react').Component
const connect = require('react-redux').connect const connect = require('react-redux').connect
const h = require('react-hyperscript') const h = require('react-hyperscript')
const { checkFeatureToggle } = require('../lib/feature-toggle-utils')
const actions = require('./actions') const actions = require('./actions')
// mascara // mascara
const MascaraFirstTime = require('../../mascara/src/app/first-time').default 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') const NewKeyChainScreen = require('./new-keychain')
// accounts // accounts
const MainContainer = require('./main-container') const MainContainer = require('./main-container')
const SendTransactionScreen = require('./send')
const SendTransactionScreen2 = require('./components/send/send-v2-container') const SendTransactionScreen2 = require('./components/send/send-v2-container')
const SendTokenScreen = require('./components/send-token')
const ConfirmTxScreen = require('./conf-tx') const ConfirmTxScreen = require('./conf-tx')
// notice // notice
const NoticeScreen = require('./components/notice') const NoticeScreen = require('./components/notice')
@ -27,7 +24,6 @@ const WalletView = require('./components/wallet-view')
const Settings = require('./settings') const Settings = require('./settings')
const AddTokenScreen = require('./add-token') const AddTokenScreen = require('./add-token')
const Import = require('./accounts/import') const Import = require('./accounts/import')
const InfoScreen = require('./info')
const Loading = require('./components/loading') const Loading = require('./components/loading')
const NetworkIndicator = require('./components/network') const NetworkIndicator = require('./components/network')
const Identicon = require('./components/identicon') 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 ReactCSSTransitionGroup = require('react-addons-css-transition-group')
const NetworkDropdown = require('./components/dropdowns/network-dropdown') const NetworkDropdown = require('./components/dropdowns/network-dropdown')
const AccountMenu = require('./components/account-menu') const AccountMenu = require('./components/account-menu')
const QrView = require('./components/qr-code')
// Global Modals // Global Modals
const Modal = require('./components/modals/index').Modal const Modal = require('./components/modals/index').Modal
@ -148,7 +145,7 @@ App.prototype.render = function () {
(isLoading || isLoadingNetwork) && h(Loading, { (isLoading || isLoadingNetwork) && h(Loading, {
loadingMessage: loadMessage, loadingMessage: loadMessage,
}), }),
// this.renderLoadingIndicator({ isLoading, isLoadingNetwork, loadMessage }), // this.renderLoadingIndicator({ isLoading, isLoadingNetwork, loadMessage }),
// content // content
@ -228,8 +225,6 @@ App.prototype.renderAppBar = function () {
} }
const props = this.props const props = this.props
const state = this.state || {}
const isNetworkMenuOpen = state.isNetworkMenuOpen || false
const {isMascara, isOnboarding} = props const {isMascara, isOnboarding} = props
// Do not render header if user is in mascara onboarding // Do not render header if user is in mascara onboarding

View File

@ -32,6 +32,7 @@ function mapDispatchToProps (dispatch) {
}, },
lockMetamask: () => { lockMetamask: () => {
dispatch(actions.lockMetamask()) dispatch(actions.lockMetamask())
dispatch(actions.displayWarning(null))
dispatch(actions.toggleAccountMenu()) dispatch(actions.toggleAccountMenu())
}, },
showConfigPage: () => { showConfigPage: () => {

View File

@ -2,7 +2,7 @@ const Component = require('react').Component
const h = require('react-hyperscript') const h = require('react-hyperscript')
const inherits = require('util').inherits const inherits = require('util').inherits
const InputNumber = require('../input-number.js') const InputNumber = require('../input-number.js')
const GasSlider = require('./gas-slider.js') // const GasSlider = require('./gas-slider.js')
module.exports = GasModalCard module.exports = GasModalCard
@ -13,8 +13,7 @@ function GasModalCard () {
GasModalCard.prototype.render = function () { GasModalCard.prototype.render = function () {
const { const {
memo, // memo,
identities,
onChange, onChange,
unitLabel, unitLabel,
value, value,
@ -22,7 +21,7 @@ GasModalCard.prototype.render = function () {
// max, // max,
step, step,
title, title,
copy copy,
} = this.props } = this.props
return h('div.send-v2__gas-modal-card', [ return h('div.send-v2__gas-modal-card', [
@ -48,8 +47,8 @@ GasModalCard.prototype.render = function () {
// min, // min,
// onChange, // onChange,
// }), // }),
]) ])
} }

View File

@ -1,50 +1,50 @@
const Component = require('react').Component // const Component = require('react').Component
const h = require('react-hyperscript') // const h = require('react-hyperscript')
const inherits = require('util').inherits // const inherits = require('util').inherits
module.exports = GasSlider // module.exports = GasSlider
inherits(GasSlider, Component) // inherits(GasSlider, Component)
function GasSlider () { // function GasSlider () {
Component.call(this) // Component.call(this)
} // }
GasSlider.prototype.render = function () { // GasSlider.prototype.render = function () {
const { // const {
memo, // memo,
identities, // identities,
onChange, // onChange,
unitLabel, // unitLabel,
value, // value,
id, // id,
step, // step,
max, // max,
min, // min,
} = this.props // } = this.props
return h('div.gas-slider', [ // return h('div.gas-slider', [
h('input.gas-slider__input', { // h('input.gas-slider__input', {
type: 'range', // type: 'range',
step, // step,
max, // max,
min, // min,
value, // value,
id: 'gasSlider', // id: 'gasSlider',
onChange: event => onChange(event.target.value), // onChange: event => onChange(event.target.value),
}, []), // }, []),
h('div.gas-slider__bar', [ // h('div.gas-slider__bar', [
h('div.gas-slider__low'), // h('div.gas-slider__low'),
h('div.gas-slider__mid'), // h('div.gas-slider__mid'),
h('div.gas-slider__high'), // h('div.gas-slider__high'),
]), // ]),
]) // ])
} // }

View File

@ -58,7 +58,7 @@ function mapDispatchToProps (dispatch) {
} }
} }
function getOriginalState(props) { function getOriginalState (props) {
const gasPrice = props.gasPrice || MIN_GAS_PRICE_DEC const gasPrice = props.gasPrice || MIN_GAS_PRICE_DEC
const gasLimit = props.gasLimit || MIN_GAS_LIMIT_DEC const gasLimit = props.gasLimit || MIN_GAS_LIMIT_DEC
@ -90,7 +90,7 @@ CustomizeGasModal.prototype.save = function (gasPrice, gasLimit, gasTotal) {
updateGasPrice, updateGasPrice,
updateGasLimit, updateGasLimit,
hideModal, hideModal,
updateGasTotal updateGasTotal,
} = this.props } = this.props
updateGasPrice(gasPrice) updateGasPrice(gasPrice)
@ -126,9 +126,9 @@ CustomizeGasModal.prototype.validate = function ({ gasTotal, gasLimit }) {
}) })
if (!balanceIsSufficient) { if (!balanceIsSufficient) {
error = 'Insufficient balance for current gas total' error = 'Insufficient balance for current gas total'
} }
const gasLimitTooLow = gasLimit && conversionGreaterThan( const gasLimitTooLow = gasLimit && conversionGreaterThan(
{ {
value: MIN_GAS_LIMIT_DEC, value: MIN_GAS_LIMIT_DEC,
@ -142,7 +142,7 @@ CustomizeGasModal.prototype.validate = function ({ gasTotal, gasLimit }) {
) )
if (gasLimitTooLow) { if (gasLimitTooLow) {
error = 'Gas limit must be at least 21000' error = 'Gas limit must be at least 21000'
} }
this.setState({ error }) this.setState({ error })
@ -190,7 +190,7 @@ CustomizeGasModal.prototype.convertAndSetGasPrice = function (newGasPrice) {
} }
CustomizeGasModal.prototype.render = function () { CustomizeGasModal.prototype.render = function () {
const { hideModal, conversionRate } = this.props const { hideModal } = this.props
const { gasPrice, gasLimit, gasTotal, error } = this.state const { gasPrice, gasLimit, gasTotal, error } = this.state
const convertedGasPrice = conversionUtil(gasPrice, { const convertedGasPrice = conversionUtil(gasPrice, {
@ -219,12 +219,12 @@ CustomizeGasModal.prototype.render = function () {
]), ]),
h('div.send-v2__customize-gas__body', {}, [ h('div.send-v2__customize-gas__body', {}, [
h(GasModalCard, { h(GasModalCard, {
value: convertedGasPrice, value: convertedGasPrice,
min: MIN_GAS_PRICE_GWEI, min: MIN_GAS_PRICE_GWEI,
// max: 1000, // max: 1000,
step: 1, step: MIN_GAS_PRICE_GWEI,
onChange: value => this.convertAndSetGasPrice(value), onChange: value => this.convertAndSetGasPrice(value),
title: 'Gas Price (GWEI)', title: 'Gas Price (GWEI)',
copy: 'We calculate the suggested gas prices based on network success rates.', 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__error-message', [
error, error,
]), ]),
h('div.send-v2__customize-gas__revert', { h('div.send-v2__customize-gas__revert', {
onClick: () => this.revert(), onClick: () => this.revert(),
}, ['Revert']), }, ['Revert']),
@ -260,7 +260,7 @@ CustomizeGasModal.prototype.render = function () {
h(`div.send-v2__customize-gas__save${error ? '__error' : ''}`, { h(`div.send-v2__customize-gas__save${error ? '__error' : ''}`, {
onClick: () => !error && this.save(gasPrice, gasLimit, gasTotal), onClick: () => !error && this.save(gasPrice, gasLimit, gasTotal),
}, ['SAVE']), }, ['SAVE']),
]) ]),
]), ]),

View File

@ -1,7 +1,6 @@
const Component = require('react').Component const Component = require('react').Component
const h = require('react-hyperscript') const h = require('react-hyperscript')
const inherits = require('util').inherits const inherits = require('util').inherits
const Identicon = require('../identicon')
const AccountListItem = require('../send/account-list-item') const AccountListItem = require('../send/account-list-item')
module.exports = AccountDropdownMini module.exports = AccountDropdownMini
@ -38,13 +37,13 @@ AccountDropdownMini.prototype.renderDropdown = function () {
...accounts.map(account => h(AccountListItem, { ...accounts.map(account => h(AccountListItem, {
account, account,
displayBalance: false, displayBalance: false,
displayAddress: false, displayAddress: false,
handleClick: () => { handleClick: () => {
onSelect(account) onSelect(account)
closeDropdown() closeDropdown()
}, },
icon: this.getListItemIcon(account, selectedAccount), icon: this.getListItemIcon(account, selectedAccount),
})) })),
]), ]),
@ -53,10 +52,8 @@ AccountDropdownMini.prototype.renderDropdown = function () {
AccountDropdownMini.prototype.render = function () { AccountDropdownMini.prototype.render = function () {
const { const {
accounts,
selectedAccount, selectedAccount,
openDropdown, openDropdown,
closeDropdown,
dropdownOpen, dropdownOpen,
} = this.props } = this.props
@ -67,12 +64,12 @@ AccountDropdownMini.prototype.render = function () {
handleClick: openDropdown, handleClick: openDropdown,
displayBalance: false, displayBalance: false,
displayAddress: 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(), dropdownOpen && this.renderDropdown(),
]) ])
} }

View File

@ -19,7 +19,7 @@ AccountOptionsDropdown.prototype.render = function () {
return h(AccountDropdowns, { return h(AccountDropdowns, {
enableAccountOptions: true, enableAccountOptions: true,
enableAccountsSelector: false, enableAccountsSelector: false,
selected: selectedAddress, selected,
network, network,
identities, identities,
style: style || {}, style: style || {},

View File

@ -19,7 +19,7 @@ AccountSelectionDropdown.prototype.render = function () {
return h(AccountDropdowns, { return h(AccountDropdowns, {
enableAccountOptions: false, enableAccountOptions: false,
enableAccountsSelector: true, enableAccountsSelector: true,
selected: selectedAddress, selected,
network, network,
identities, identities,
style: style || {}, style: style || {},

View File

@ -425,6 +425,21 @@ AccountDropdowns.propTypes = {
identities: PropTypes.objectOf(PropTypes.object), identities: PropTypes.objectOf(PropTypes.object),
selected: PropTypes.string, selected: PropTypes.string,
keyrings: PropTypes.array, 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) => { const mapDispatchToProps = (dispatch) => {

View File

@ -68,6 +68,7 @@ Dropdown.propTypes = {
onClickOutside: PropTypes.func, onClickOutside: PropTypes.func,
innerStyle: PropTypes.object, innerStyle: PropTypes.object,
useCssTransition: PropTypes.bool, useCssTransition: PropTypes.bool,
containerClassName: PropTypes.string,
} }
class DropdownMenuItem extends Component { class DropdownMenuItem extends Component {

View File

@ -1,4 +1,5 @@
const { Component, PropTypes } = require('react') const { Component } = require('react')
const PropTypes = require('react').PropTypes
const h = require('react-hyperscript') const h = require('react-hyperscript')
const classnames = require('classnames') const classnames = require('classnames')
const R = require('ramda') const R = require('ramda')

View File

@ -10,7 +10,7 @@ function mapDispatchToProps (dispatch) {
return { return {
showHideTokenConfirmationModal: (token) => { showHideTokenConfirmationModal: (token) => {
dispatch(actions.showModal({ name: 'HIDE_TOKEN_CONFIRMATION', 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__container', {}, [
h('div.token-menu-dropdown__options', {}, [ h('div.token-menu-dropdown__options', {}, [
h('div.token-menu-dropdown__option', { h('div.token-menu-dropdown__option', {
onClick: (e) => { onClick: (e) => {
e.stopPropagation() e.stopPropagation()
showHideTokenConfirmationModal(this.props.token) showHideTokenConfirmationModal(this.props.token)
this.props.onClose() this.props.onClose()
}, },
}, 'Hide Token') }, 'Hide Token'),
]), ]),
]), ]),

View File

@ -1,7 +1,12 @@
const Component = require('react').Component const Component = require('react').Component
const h = require('react-hyperscript') const h = require('react-hyperscript')
const inherits = require('util').inherits const inherits = require('util').inherits
const { addCurrencies } = require('../conversion-util') const {
addCurrencies,
conversionGTE,
conversionLTE,
toNegative,
} = require('../conversion-util')
module.exports = InputNumber module.exports = InputNumber
@ -17,8 +22,21 @@ InputNumber.prototype.setValue = function (newValue) {
newValue = Number(fixed ? newValue.toFixed(4) : 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) 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', { h('input.customize-gas-input', {
placeholder, placeholder,
type: 'number', type: 'number',
value: value, value,
onChange: (e) => this.setValue(e.target.value), onChange: (e) => this.setValue(e.target.value),
}), }),
h('span.gas-tooltip-input-detail', {}, [unitLabel]), h('span.gas-tooltip-input-detail', {}, [unitLabel]),
@ -39,7 +57,7 @@ InputNumber.prototype.render = function () {
}), }),
h('i.fa.fa-angle-down', { h('i.fa.fa-angle-down', {
style: { cursor: 'pointer' }, style: { cursor: 'pointer' },
onClick: () => this.setValue(addCurrencies(value, step * -1)), onClick: () => this.setValue(addCurrencies(value, toNegative(step))),
}), }),
]), ]),
]) ])

View File

@ -1,5 +1,6 @@
const { Component } = require('react') const { Component } = require('react')
const h = require('react-hyperscript') const h = require('react-hyperscript')
const PropTypes = require('react').PropTypes
class LoadingIndicator extends Component { class LoadingIndicator extends Component {
renderMessage () { renderMessage () {
@ -35,4 +36,8 @@ class LoadingIndicator extends Component {
} }
} }
LoadingIndicator.propTypes = {
loadingMessage: PropTypes.string,
}
module.exports = LoadingIndicator module.exports = LoadingIndicator

View File

@ -4,7 +4,7 @@ const inherits = require('util').inherits
const connect = require('react-redux').connect const connect = require('react-redux').connect
const actions = require('../../actions') const actions = require('../../actions')
const AccountModalContainer = require('./account-modal-container') const AccountModalContainer = require('./account-modal-container')
const { getSelectedIdentity, getSelectedAddress } = require('../../selectors') const { getSelectedIdentity } = require('../../selectors')
const genAccountLink = require('../../../lib/account-link.js') const genAccountLink = require('../../../lib/account-link.js')
const QrView = require('../qr-code') const QrView = require('../qr-code')
const EditableLabel = require('../editable-label') const EditableLabel = require('../editable-label')

View File

@ -92,7 +92,6 @@ ExportPrivateKeyModal.prototype.renderButtons = function (privateKey, password,
ExportPrivateKeyModal.prototype.render = function () { ExportPrivateKeyModal.prototype.render = function () {
const { const {
selectedIdentity, selectedIdentity,
network,
warning, warning,
showAccountDetailModal, showAccountDetailModal,
hideModal, hideModal,

View File

@ -220,7 +220,7 @@ Modal.prototype.render = function () {
const children = modal.contents const children = modal.contents
const modalStyle = modal[isMobileView() ? 'mobileModalStyle' : 'laptopModalStyle'] const modalStyle = modal[isMobileView() ? 'mobileModalStyle' : 'laptopModalStyle']
const contentStyle = modal.contentStyle || {}; const contentStyle = modal.contentStyle || {}
return h(FadeModal, return h(FadeModal,
{ {

View File

@ -1,17 +1,88 @@
const Component = require('react').Component const { Component } = require('react')
const PropTypes = require('prop-types')
const h = require('react-hyperscript') const h = require('react-hyperscript')
const inherits = require('util').inherits const { connect } = require('react-redux')
const connect = require('react-redux').connect
const actions = require('../../actions') const actions = require('../../actions')
function mapStateToProps (state) { class NewAccountModal extends Component {
return { constructor (props) {
network: state.metamask.network, super(props)
address: state.metamask.selectedAddress, 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',
]),
]),
]),
])
} }
} }
function mapDispatchToProps (dispatch) { 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,
address: selectedAddress,
numberOfExistingAccounts,
}
}
const mapDispatchToProps = dispatch => {
return { return {
toCoinbase: (address) => { toCoinbase: (address) => {
dispatch(actions.buyEth({ network: '1', address, amount: 0 })) 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) 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',
]),
]),
]),
])
}

View File

@ -35,6 +35,6 @@ ShapeshiftDepositTxModal.prototype.render = function () {
}, [ }, [
h('div', {}, [ h('div', {}, [
h(QrView, {key: 'qr', Qr}), h(QrView, {key: 'qr', Qr}),
]) ]),
]) ])
} }

View File

@ -1,6 +1,6 @@
const Component = require('react').Component const Component = require('react').Component
const h = require('react-hyperscript') const h = require('react-hyperscript')
const classnames = require('classnames'); const classnames = require('classnames')
const inherits = require('util').inherits const inherits = require('util').inherits
const NetworkDropdownIcon = require('./dropdowns/components/network-dropdown-icon') const NetworkDropdownIcon = require('./dropdowns/components/network-dropdown-icon')

View File

@ -10,9 +10,7 @@ const BN = ethUtil.BN
const hexToBn = require('../../../../app/scripts/lib/hex-to-bn') const hexToBn = require('../../../../app/scripts/lib/hex-to-bn')
const { conversionUtil } = require('../../conversion-util') const { conversionUtil } = require('../../conversion-util')
const MIN_GAS_PRICE_GWEI_BN = new BN(1) const { MIN_GAS_PRICE_HEX } = require('../send/send-constants')
const GWEI_FACTOR = new BN(1e9)
const MIN_GAS_PRICE_BN = MIN_GAS_PRICE_GWEI_BN.mul(GWEI_FACTOR)
module.exports = connect(mapStateToProps, mapDispatchToProps)(ConfirmDeployContract) module.exports = connect(mapStateToProps, mapDispatchToProps)(ConfirmDeployContract)
@ -166,7 +164,7 @@ ConfirmDeployContract.prototype.getGasFee = function () {
const gasBn = hexToBn(gas) const gasBn = hexToBn(gas)
// Gas Price // 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 gasPriceBn = hexToBn(gasPrice)
const txFeeBn = gasBn.mul(gasPriceBn) const txFeeBn = gasBn.mul(gasPriceBn)

View File

@ -10,9 +10,7 @@ const BN = ethUtil.BN
const hexToBn = require('../../../../app/scripts/lib/hex-to-bn') const hexToBn = require('../../../../app/scripts/lib/hex-to-bn')
const { conversionUtil, addCurrencies } = require('../../conversion-util') const { conversionUtil, addCurrencies } = require('../../conversion-util')
const MIN_GAS_PRICE_GWEI_BN = new BN(1) const { MIN_GAS_PRICE_HEX } = require('../send/send-constants')
const GWEI_FACTOR = new BN(1e9)
const MIN_GAS_PRICE_BN = MIN_GAS_PRICE_GWEI_BN.mul(GWEI_FACTOR)
module.exports = connect(mapStateToProps, mapDispatchToProps)(ConfirmSendEther) module.exports = connect(mapStateToProps, mapDispatchToProps)(ConfirmSendEther)
@ -93,7 +91,7 @@ ConfirmSendEther.prototype.getGasFee = function () {
// const safeGasLimit = safeGasLimitBN.toString(10) // const safeGasLimit = safeGasLimitBN.toString(10)
// Gas Price // 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 gasPriceBn = hexToBn(gasPrice)
const txFeeBn = gasBn.mul(gasPriceBn) const txFeeBn = gasBn.mul(gasPriceBn)

View File

@ -10,19 +10,15 @@ const clone = require('clone')
const Identicon = require('../identicon') const Identicon = require('../identicon')
const ethUtil = require('ethereumjs-util') const ethUtil = require('ethereumjs-util')
const BN = ethUtil.BN const BN = ethUtil.BN
const hexToBn = require('../../../../app/scripts/lib/hex-to-bn')
const { const {
conversionUtil, conversionUtil,
multiplyCurrencies, multiplyCurrencies,
addCurrencies, addCurrencies,
} = require('../../conversion-util') } = require('../../conversion-util')
const MIN_GAS_PRICE_GWEI_BN = new BN(1) const { MIN_GAS_PRICE_HEX } = require('../send/send-constants')
const GWEI_FACTOR = new BN(1e9)
const MIN_GAS_PRICE_BN = MIN_GAS_PRICE_GWEI_BN.mul(GWEI_FACTOR)
const { const {
getSelectedTokenExchangeRate,
getTokenExchangeRate, getTokenExchangeRate,
getSelectedAddress, getSelectedAddress,
} = require('../../selectors') } = require('../../selectors')
@ -38,7 +34,6 @@ function mapStateToProps (state, ownProps) {
identities, identities,
currentCurrency, currentCurrency,
} = state.metamask } = state.metamask
const accounts = state.metamask.accounts
const selectedAddress = getSelectedAddress(state) const selectedAddress = getSelectedAddress(state)
const tokenExchangeRate = getTokenExchangeRate(state, symbol) const tokenExchangeRate = getTokenExchangeRate(state, symbol)
@ -99,7 +94,7 @@ ConfirmSendToken.prototype.getGasFee = function () {
const { decimals } = token const { decimals } = token
const gas = txParams.gas 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, { const gasTotal = multiplyCurrencies(gas, gasPrice, {
multiplicandBase: 16, multiplicandBase: 16,
multiplierBase: 16, multiplierBase: 16,
@ -149,7 +144,7 @@ ConfirmSendToken.prototype.getData = function () {
const { value } = params[0] || {} const { value } = params[0] || {}
const txMeta = this.gatherTxMeta() const txMeta = this.gatherTxMeta()
const txParams = txMeta.txParams || {} const txParams = txMeta.txParams || {}
return { return {
from: { from: {
address: txParams.from, address: txParams.from,
@ -247,7 +242,6 @@ ConfirmSendToken.prototype.renderTotalPlusGas = function () {
ConfirmSendToken.prototype.render = function () { ConfirmSendToken.prototype.render = function () {
const { backToAccountDetail, selectedAddress } = this.props const { backToAccountDetail, selectedAddress } = this.props
const txMeta = this.gatherTxMeta() const txMeta = this.gatherTxMeta()
const txParams = txMeta.txParams || {}
const { const {
from: { from: {

View File

@ -144,16 +144,15 @@ SendTokenScreen.prototype.validate = function () {
} }
SendTokenScreen.prototype.setErrorsFor = function (field) { SendTokenScreen.prototype.setErrorsFor = function (field) {
const { balance, selectedToken } = this.props
const { errors: previousErrors } = this.state const { errors: previousErrors } = this.state
const { const {
isValid, isValid,
errors: newErrors errors: newErrors,
} = this.validate() } = this.validate()
const nextErrors = Object.assign({}, previousErrors, { const nextErrors = Object.assign({}, previousErrors, {
[field]: newErrors[field] || null [field]: newErrors[field] || null,
}) })
if (!isValid) { if (!isValid) {
@ -167,7 +166,7 @@ SendTokenScreen.prototype.setErrorsFor = function (field) {
SendTokenScreen.prototype.clearErrorsFor = function (field) { SendTokenScreen.prototype.clearErrorsFor = function (field) {
const { errors: previousErrors } = this.state const { errors: previousErrors } = this.state
const nextErrors = Object.assign({}, previousErrors, { const nextErrors = Object.assign({}, previousErrors, {
[field]: null [field]: null,
}) })
this.setState({ this.setState({
@ -429,7 +428,7 @@ SendTokenScreen.prototype.render = function () {
this.renderAmountInput(), this.renderAmountInput(),
this.renderGasInput(), this.renderGasInput(),
this.renderMemoInput(), 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', [ h('div.send-screen-input-wrapper__error-message', [
warning, warning,
]) ])

View File

@ -68,4 +68,4 @@ AccountListItem.prototype.render = function () {
}, name), }, name),
]) ])
} }

View File

@ -1,7 +1,6 @@
const Component = require('react').Component const Component = require('react').Component
const h = require('react-hyperscript') const h = require('react-hyperscript')
const inherits = require('util').inherits const inherits = require('util').inherits
const Identicon = require('../identicon')
const { conversionUtil, multiplyCurrencies } = require('../../conversion-util') const { conversionUtil, multiplyCurrencies } = require('../../conversion-util')
module.exports = CurrencyDisplay module.exports = CurrencyDisplay
@ -48,8 +47,6 @@ CurrencyDisplay.prototype.render = function () {
conversionRate, conversionRate,
primaryCurrency, primaryCurrency,
convertedCurrency, convertedCurrency,
convertedPrefix = '',
placeholder = '0',
readOnly = false, readOnly = false,
inError = false, inError = false,
value: initValue, value: initValue,
@ -74,7 +71,7 @@ CurrencyDisplay.prototype.render = function () {
conversionRate, conversionRate,
}) })
const inputSizeMultiplier = readOnly ? 1 : 1.2; const inputSizeMultiplier = readOnly ? 1 : 1.2
return h('div', { return h('div', {
className, className,
@ -98,15 +95,13 @@ CurrencyDisplay.prototype.render = function () {
if (newValue === '') { if (newValue === '') {
newValue = '0' newValue = '0'
} } else if (newValue.match(/^0[1-9]$/)) {
else if (newValue.match(/^0[1-9]$/)) {
newValue = newValue.match(/[1-9]/)[0] newValue = newValue.match(/[1-9]/)[0]
} }
if (newValue && !isValidInput(newValue)) { if (newValue && !isValidInput(newValue)) {
event.preventDefault() event.preventDefault()
} } else {
else {
validate(this.getAmount(newValue)) validate(this.getAmount(newValue))
this.setState({ value: newValue }) this.setState({ value: newValue })
} }
@ -125,6 +120,6 @@ CurrencyDisplay.prototype.render = function () {
}, `${convertedValue} ${convertedCurrency.toUpperCase()}`), }, `${convertedValue} ${convertedCurrency.toUpperCase()}`),
]) ])
} }

View File

@ -30,8 +30,8 @@ EthFeeDisplay.prototype.render = function () {
color: '#5d5d5d', color: '#5d5d5d',
fontSize: '16px', fontSize: '16px',
fontFamily: 'DIN OT', fontFamily: 'DIN OT',
lineHeight: '22.4px' lineHeight: '22.4px',
} },
}) })
} }

View File

@ -1,7 +1,6 @@
const Component = require('react').Component const Component = require('react').Component
const h = require('react-hyperscript') const h = require('react-hyperscript')
const inherits = require('util').inherits const inherits = require('util').inherits
const Identicon = require('../identicon')
const AccountListItem = require('./account-list-item') const AccountListItem = require('./account-list-item')
module.exports = FromDropdown module.exports = FromDropdown
@ -36,13 +35,13 @@ FromDropdown.prototype.renderDropdown = function () {
h('div.send-v2__from-dropdown__list', {}, [ h('div.send-v2__from-dropdown__list', {}, [
...accounts.map(account => h(AccountListItem, { ...accounts.map(account => h(AccountListItem, {
account, account,
handleClick: () => { handleClick: () => {
onSelect(account) onSelect(account)
closeDropdown() closeDropdown()
}, },
icon: this.getListItemIcon(account, selectedAccount), icon: this.getListItemIcon(account, selectedAccount),
})) })),
]), ]),
@ -51,10 +50,8 @@ FromDropdown.prototype.renderDropdown = function () {
FromDropdown.prototype.render = function () { FromDropdown.prototype.render = function () {
const { const {
accounts,
selectedAccount, selectedAccount,
openDropdown, openDropdown,
closeDropdown,
dropdownOpen, dropdownOpen,
} = this.props } = this.props
@ -63,12 +60,12 @@ FromDropdown.prototype.render = function () {
h(AccountListItem, { h(AccountListItem, {
account: selectedAccount, account: selectedAccount,
handleClick: openDropdown, 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(), dropdownOpen && this.renderDropdown(),
]) ])
} }

View File

@ -23,21 +23,20 @@ GasFeeDisplay.prototype.render = function () {
gasTotal gasTotal
? h(CurrencyDisplay, { ? h(CurrencyDisplay, {
primaryCurrency: 'ETH', primaryCurrency,
convertedCurrency, convertedCurrency,
value: gasTotal, value: gasTotal,
conversionRate, conversionRate,
convertedPrefix: '$', convertedPrefix: '$',
readOnly: true, readOnly: true,
}) })
: h('div.currency-display', 'Loading...') : h('div.currency-display', 'Loading...'),
,
h('div.send-v2__sliders-icon-container', { h('div.send-v2__sliders-icon-container', {
onClick, onClick,
}, [ }, [
h('i.fa.fa-sliders.send-v2__sliders-icon'), h('i.fa.fa-sliders.send-v2__sliders-icon'),
]) ]),
]) ])
} }

View File

@ -1,33 +1,33 @@
const Component = require('react').Component // const Component = require('react').Component
const h = require('react-hyperscript') // const h = require('react-hyperscript')
const inherits = require('util').inherits // const inherits = require('util').inherits
const Identicon = require('../identicon') // const Identicon = require('../identicon')
module.exports = MemoTextArea // module.exports = MemoTextArea
inherits(MemoTextArea, Component) // inherits(MemoTextArea, Component)
function MemoTextArea () { // function MemoTextArea () {
Component.call(this) // Component.call(this)
} // }
MemoTextArea.prototype.render = function () { // MemoTextArea.prototype.render = function () {
const { memo, identities, onChange } = this.props // const { memo, identities, onChange } = this.props
return h('div.send-v2__memo-text-area', [ // return h('div.send-v2__memo-text-area', [
h('textarea.send-v2__memo-text-area__input', { // h('textarea.send-v2__memo-text-area__input', {
placeholder: 'Optional', // placeholder: 'Optional',
value: memo, // value: memo,
onChange, // onChange,
// onBlur: () => { // // onBlur: () => {
// this.setErrorsFor('memo') // // this.setErrorsFor('memo')
// }, // // },
onFocus: event => { // onFocus: event => {
// this.clearErrorsFor('memo') // // this.clearErrorsFor('memo')
}, // },
}), // }),
]) // ])
} // }

View File

@ -1,20 +1,18 @@
const Identicon = require('../identicon') const ethUtil = require('ethereumjs-util')
const { multiplyCurrencies } = require('../../conversion-util') const { conversionUtil, multiplyCurrencies } = require('../../conversion-util')
const MIN_GAS_PRICE_GWEI = '1' const MIN_GAS_PRICE_HEX = (100000000).toString(16)
const GWEI_FACTOR = '1e9' const MIN_GAS_PRICE_DEC = '100000000'
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_LIMIT_HEX = (21000).toString(16) const MIN_GAS_LIMIT_HEX = (21000).toString(16)
const MIN_GAS_LIMIT_DEC = 21000 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, { const MIN_GAS_TOTAL = multiplyCurrencies(MIN_GAS_LIMIT_HEX, MIN_GAS_PRICE_HEX, {
toNumericBase: 'hex', toNumericBase: 'hex',
multiplicandBase: 16, multiplicandBase: 16,

View File

@ -1,6 +1,6 @@
const { addCurrencies, conversionGreaterThan } = require('../../conversion-util') const { addCurrencies, conversionGreaterThan } = require('../../conversion-util')
function isBalanceSufficient({ function isBalanceSufficient ({
amount, amount,
gasTotal, gasTotal,
balance, balance,
@ -27,7 +27,6 @@ function isBalanceSufficient({
fromNumericBase: 'hex', fromNumericBase: 'hex',
conversionRate: amountConversionRate, conversionRate: amountConversionRate,
fromCurrency: selectedToken || primaryCurrency, fromCurrency: selectedToken || primaryCurrency,
conversionRate: amountConversionRate,
}, },
) )
@ -36,4 +35,4 @@ function isBalanceSufficient({
module.exports = { module.exports = {
isBalanceSufficient, isBalanceSufficient,
} }

View File

@ -3,17 +3,12 @@ const actions = require('../../actions')
const abi = require('ethereumjs-abi') const abi = require('ethereumjs-abi')
const SendEther = require('../../send-v2') const SendEther = require('../../send-v2')
const { multiplyCurrencies } = require('../../conversion-util')
const { const {
accountsWithSendEtherInfoSelector, accountsWithSendEtherInfoSelector,
getCurrentAccountWithSendEtherInfo, getCurrentAccountWithSendEtherInfo,
conversionRateSelector, conversionRateSelector,
getSelectedToken, getSelectedToken,
getSelectedTokenExchangeRate,
getSelectedAddress, getSelectedAddress,
getGasPrice,
getGasLimit,
getAddressBook, getAddressBook,
getSendFrom, getSendFrom,
getCurrentCurrency, getCurrentCurrency,
@ -26,12 +21,11 @@ function mapStateToProps (state) {
const fromAccounts = accountsWithSendEtherInfoSelector(state) const fromAccounts = accountsWithSendEtherInfoSelector(state)
const selectedAddress = getSelectedAddress(state) const selectedAddress = getSelectedAddress(state)
const selectedToken = getSelectedToken(state) const selectedToken = getSelectedToken(state)
const tokenExchangeRates = state.metamask.tokenExchangeRates
const conversionRate = conversionRateSelector(state) const conversionRate = conversionRateSelector(state)
let data; let data
let primaryCurrency; let primaryCurrency
let tokenToFiatRate; let tokenToFiatRate
if (selectedToken) { if (selectedToken) {
data = Array.prototype.map.call( data = Array.prototype.map.call(
abi.rawEncode(['address', 'uint256'], [selectedAddress, '0x0']), abi.rawEncode(['address', 'uint256'], [selectedAddress, '0x0']),
@ -76,6 +70,6 @@ function mapDispatchToProps (dispatch) {
updateSendMemo: newMemo => dispatch(actions.updateSendMemo(newMemo)), updateSendMemo: newMemo => dispatch(actions.updateSendMemo(newMemo)),
updateSendErrors: newError => dispatch(actions.updateSendErrors(newError)), updateSendErrors: newError => dispatch(actions.updateSendErrors(newError)),
goHome: () => dispatch(actions.goHome()), goHome: () => dispatch(actions.goHome()),
clearSend: () => dispatch(actions.clearSend()) clearSend: () => dispatch(actions.clearSend()),
} }
} }

View File

@ -1,7 +1,6 @@
const Component = require('react').Component const Component = require('react').Component
const h = require('react-hyperscript') const h = require('react-hyperscript')
const inherits = require('util').inherits const inherits = require('util').inherits
const Identicon = require('../identicon')
const AccountListItem = require('./account-list-item') const AccountListItem = require('./account-list-item')
module.exports = ToAutoComplete module.exports = ToAutoComplete
@ -23,7 +22,6 @@ ToAutoComplete.prototype.getListItemIcon = function (listItemAddress, toAddress)
ToAutoComplete.prototype.renderDropdown = function () { ToAutoComplete.prototype.renderDropdown = function () {
const { const {
accounts,
closeDropdown, closeDropdown,
onChange, onChange,
to, to,
@ -39,15 +37,15 @@ ToAutoComplete.prototype.renderDropdown = function () {
h('div.send-v2__from-dropdown__list', {}, [ h('div.send-v2__from-dropdown__list', {}, [
...accountsToRender.map(account => h(AccountListItem, { ...accountsToRender.map(account => h(AccountListItem, {
account, account,
handleClick: () => { handleClick: () => {
onChange(account.address) onChange(account.address)
closeDropdown() closeDropdown()
}, },
icon: this.getListItemIcon(account.address, to), icon: this.getListItemIcon(account.address, to),
displayBalance: false, displayBalance: false,
displayAddress: true, displayAddress: true,
})) })),
]), ]),
@ -69,8 +67,7 @@ ToAutoComplete.prototype.handleInputEvent = function (event = {}, cb) {
this.setState({ accountsToRender: [] }) this.setState({ accountsToRender: [] })
event.target && event.target.select() event.target && event.target.select()
closeDropdown() closeDropdown()
} } else {
else {
this.setState({ accountsToRender: matchingAccounts }) this.setState({ accountsToRender: matchingAccounts })
openDropdown() openDropdown()
} }
@ -86,9 +83,6 @@ ToAutoComplete.prototype.componentDidUpdate = function (nextProps, nextState) {
ToAutoComplete.prototype.render = function () { ToAutoComplete.prototype.render = function () {
const { const {
to, to,
accounts,
openDropdown,
closeDropdown,
dropdownOpen, dropdownOpen,
onChange, onChange,
inError, inError,
@ -98,13 +92,13 @@ ToAutoComplete.prototype.render = function () {
h('input.send-v2__to-autocomplete__input', { h('input.send-v2__to-autocomplete__input', {
placeholder: 'Recipient Address', placeholder: 'Recipient Address',
className: inError ? `send-v2__error-border` : '', className: inError ? `send-v2__error-border` : '',
value: to, value: to,
onChange: event => onChange(event.target.value), onChange: event => onChange(event.target.value),
onFocus: event => this.handleInputEvent(event), onFocus: event => this.handleInputEvent(event),
style: { style: {
borderColor: inError ? 'red' : null, borderColor: inError ? 'red' : null,
} },
}), }),
!to && h(`i.fa.fa-caret-down.fa-lg.send-v2__to-autocomplete__down-caret`, { !to && h(`i.fa.fa-caret-down.fa-lg.send-v2__to-autocomplete__down-caret`, {

View File

@ -28,8 +28,8 @@ USDFeeDisplay.prototype.render = function () {
color: '#5d5d5d', color: '#5d5d5d',
fontSize: '16px', fontSize: '16px',
fontFamily: 'DIN OT', fontFamily: 'DIN OT',
lineHeight: '22.4px' lineHeight: '22.4px',
} },
}) })
} }

View File

@ -4,9 +4,9 @@ const inherits = require('util').inherits
const Identicon = require('./identicon') const Identicon = require('./identicon')
const connect = require('react-redux').connect const connect = require('react-redux').connect
const ethUtil = require('ethereumjs-util') const ethUtil = require('ethereumjs-util')
const PendingTxDetails = require('./pending-personal-msg-details') const classnames = require('classnames')
const AccountDropdownMini = require('./dropdowns/account-dropdown-mini') const AccountDropdownMini = require('./dropdowns/account-dropdown-mini')
const BinaryRenderer = require('./binary-renderer')
const actions = require('../actions') const actions = require('../actions')
const { conversionUtil } = require('../conversion-util') const { conversionUtil } = require('../conversion-util')
@ -27,13 +27,13 @@ function mapStateToProps (state) {
requester: null, requester: null,
requesterAddress: null, requesterAddress: null,
accounts: accountsWithSendEtherInfoSelector(state), accounts: accountsWithSendEtherInfoSelector(state),
conversionRate: conversionRateSelector(state) conversionRate: conversionRateSelector(state),
} }
} }
function mapDispatchToProps (dispatch) { function mapDispatchToProps (dispatch) {
return { return {
goHome: () => dispatch(actions.goHome()) goHome: () => dispatch(actions.goHome()),
} }
} }
@ -84,7 +84,7 @@ SignatureRequest.prototype.renderAccountDropdown = function () {
dropdownOpen: accountDropdownOpen, dropdownOpen: accountDropdownOpen,
openDropdown: () => this.setState({ accountDropdownOpen: true }), openDropdown: () => this.setState({ accountDropdownOpen: true }),
closeDropdown: () => this.setState({ accountDropdownOpen: false }), closeDropdown: () => this.setState({ accountDropdownOpen: false }),
}) }),
]) ])
} }
@ -113,7 +113,7 @@ SignatureRequest.prototype.renderAccountInfo = function () {
return h('div.request-signature__account-info', [ return h('div.request-signature__account-info', [
this.renderAccountDropdown(), this.renderAccountDropdown(),
this.renderRequestIcon(), this.renderRequestIcon(),
this.renderBalance(), this.renderBalance(),
@ -128,18 +128,16 @@ SignatureRequest.prototype.renderRequestIcon = function () {
h(Identicon, { h(Identicon, {
diameter: 40, diameter: 40,
address: requesterAddress, address: requesterAddress,
}) }),
]) ])
} }
SignatureRequest.prototype.renderRequestInfo = function () { SignatureRequest.prototype.renderRequestInfo = function () {
const { requester } = this.props
return h('div.request-signature__request-info', [ return h('div.request-signature__request-info', [
h('div.request-signature__headline', [ h('div.request-signature__headline', [
`Your signature is being requested`, `Your signature is being requested`,
]) ]),
]) ])
} }
@ -163,11 +161,9 @@ SignatureRequest.prototype.renderBody = function () {
if (type === 'personal_sign') { if (type === 'personal_sign') {
rows = [{ name: 'Message', value: this.msgHexToText(data) }] rows = [{ name: 'Message', value: this.msgHexToText(data) }]
} } else if (type === 'eth_signTypedData') {
else if (type === 'eth_signTypedData') {
rows = data rows = data
} } else if (type === 'eth_sign') {
else if (type === 'eth_sign') {
rows = [{ name: 'Message', value: data }] rows = [{ name: 'Message', value: data }]
notice = `Signing this message can have notice = `Signing this message can have
dangerous side effects. Only sign messages from dangerous side effects. Only sign messages from
@ -181,13 +177,18 @@ SignatureRequest.prototype.renderBody = function () {
this.renderRequestInfo(), 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', [ h('div.request-signature__rows', [
...rows.map(({ name, value }) => { ...rows.map(({ name, value }) => {
return h('div.request-signature__row', [ 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), h('div.request-signature__row-value', value),
]) ])
}), }),
@ -199,7 +200,6 @@ SignatureRequest.prototype.renderBody = function () {
SignatureRequest.prototype.renderFooter = function () { SignatureRequest.prototype.renderFooter = function () {
const { const {
goHome,
signPersonalMessage, signPersonalMessage,
signTypedMessage, signTypedMessage,
cancelPersonalMessage, cancelPersonalMessage,
@ -216,12 +216,10 @@ SignatureRequest.prototype.renderFooter = function () {
if (type === 'personal_sign') { if (type === 'personal_sign') {
cancel = cancelPersonalMessage cancel = cancelPersonalMessage
sign = signPersonalMessage sign = signPersonalMessage
} } else if (type === 'eth_signTypedData') {
else if (type === 'eth_signTypedData') {
cancel = cancelTypedMessage cancel = cancelTypedMessage
sign = signTypedMessage sign = signTypedMessage
} } else if (type === 'eth_sign') {
else if (type === 'eth_sign') {
cancel = cancelMessage cancel = cancelMessage
sign = signMessage sign = signMessage
} }

View File

@ -1,5 +1,6 @@
const { Component } = require('react') const { Component } = require('react')
const h = require('react-hyperscript') const h = require('react-hyperscript')
const PropTypes = require('react').PropTypes
const classnames = require('classnames') const classnames = require('classnames')
class TabBar extends Component { 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 module.exports = TabBar

View File

@ -6,7 +6,7 @@ const Identicon = require('./identicon')
const prefixForNetwork = require('../../lib/etherscan-prefix-for-network') const prefixForNetwork = require('../../lib/etherscan-prefix-for-network')
const selectors = require('../selectors') const selectors = require('../selectors')
const actions = require('../actions') const actions = require('../actions')
const { conversionUtil } = require('../conversion-util') const { conversionUtil, multiplyCurrencies } = require('../conversion-util')
const TokenMenuDropdown = require('./dropdowns/token-menu-dropdown.js') const TokenMenuDropdown = require('./dropdowns/token-menu-dropdown.js')
@ -17,7 +17,7 @@ function mapStateToProps (state) {
selectedTokenAddress: state.metamask.selectedTokenAddress, selectedTokenAddress: state.metamask.selectedTokenAddress,
userAddress: selectors.getSelectedAddress(state), userAddress: selectors.getSelectedAddress(state),
tokenExchangeRates: state.metamask.tokenExchangeRates, tokenExchangeRates: state.metamask.tokenExchangeRates,
ethToUSDRate: state.metamask.conversionRate, conversionRate: state.metamask.conversionRate,
sidebarOpen: state.appState.sidebarOpen, sidebarOpen: state.appState.sidebarOpen,
} }
} }
@ -61,32 +61,36 @@ TokenCell.prototype.render = function () {
setSelectedToken, setSelectedToken,
selectedTokenAddress, selectedTokenAddress,
tokenExchangeRates, tokenExchangeRates,
ethToUSDRate, conversionRate,
hideSidebar, hideSidebar,
sidebarOpen, sidebarOpen,
currentCurrency, currentCurrency,
// userAddress, // userAddress,
} = props } = props
const pair = `${symbol.toLowerCase()}_eth`;
let currentTokenToEthRate; const pair = `${symbol.toLowerCase()}_eth`
let currentTokenInFiat;
let formattedUSD = '' let currentTokenToFiatRate
let currentTokenInFiat
let formattedFiat = ''
if (tokenExchangeRates[pair]) { if (tokenExchangeRates[pair]) {
currentTokenToEthRate = tokenExchangeRates[pair].rate; currentTokenToFiatRate = multiplyCurrencies(
tokenExchangeRates[pair].rate,
conversionRate
)
currentTokenInFiat = conversionUtil(string, { currentTokenInFiat = conversionUtil(string, {
fromNumericBase: 'dec', fromNumericBase: 'dec',
fromCurrency: symbol, fromCurrency: symbol,
toCurrency: 'USD', toCurrency: currentCurrency.toUpperCase(),
numberOfDecimals: 2, numberOfDecimals: 2,
conversionRate: currentTokenToEthRate, conversionRate: currentTokenToFiatRate,
ethToUSDRate,
}) })
formattedUSD = `${currentTokenInFiat} ${currentCurrency.toUpperCase()}`; formattedFiat = `${currentTokenInFiat} ${currentCurrency.toUpperCase()}`
} }
const showFiat = Boolean(currentTokenInFiat) && currentCurrency.toUpperCase() !== symbol
return ( return (
h('div.token-list-item', { h('div.token-list-item', {
className: `token-list-item ${selectedTokenAddress === address ? 'token-list-item--active' : ''}`, 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('h.token-list-item__balance-wrapper', null, [
h('h3.token-list-item__token-balance', `${string || 0} ${symbol}`), 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: {}, style: {},
}, formattedUSD), }, formattedFiat),
]), ]),
h('i.fa.fa-ellipsis-h.fa-lg.token-list-item__ellipsis.cursor-pointer', { h('i.fa.fa-ellipsis-h.fa-lg.token-list-item__ellipsis.cursor-pointer', {

View File

@ -3,7 +3,6 @@ const h = require('react-hyperscript')
const inherits = require('util').inherits const inherits = require('util').inherits
const TokenTracker = require('eth-token-tracker') const TokenTracker = require('eth-token-tracker')
const TokenCell = require('./token-cell.js') const TokenCell = require('./token-cell.js')
const normalizeAddress = require('eth-sig-util').normalize
const connect = require('react-redux').connect const connect = require('react-redux').connect
const selectors = require('../selectors') const selectors = require('../selectors')
@ -38,6 +37,7 @@ function TokenList () {
} }
TokenList.prototype.render = function () { TokenList.prototype.render = function () {
const { userAddress } = this.props
const state = this.state const state = this.state
const { tokens, isLoading, error } = state const { tokens, isLoading, error } = state
@ -162,15 +162,15 @@ TokenList.prototype.componentWillUnmount = function () {
this.tracker.stop() this.tracker.stop()
} }
function uniqueMergeTokens (tokensA, tokensB = []) { // function uniqueMergeTokens (tokensA, tokensB = []) {
const uniqueAddresses = [] // const uniqueAddresses = []
const result = [] // const result = []
tokensA.concat(tokensB).forEach((token) => { // tokensA.concat(tokensB).forEach((token) => {
const normal = normalizeAddress(token.address) // const normal = normalizeAddress(token.address)
if (!uniqueAddresses.includes(normal)) { // if (!uniqueAddresses.includes(normal)) {
uniqueAddresses.push(normal) // uniqueAddresses.push(normal)
result.push(token) // result.push(token)
} // }
}) // })
return result // return result
} // }

View File

@ -142,7 +142,7 @@ function formatDate (date) {
} }
function renderErrorOrWarning (transaction) { function renderErrorOrWarning (transaction) {
const { status, err, warning } = transaction const { status } = transaction
// show rejected // show rejected
if (status === 'rejected') { if (status === 'rejected') {
@ -151,31 +151,31 @@ function renderErrorOrWarning (transaction) {
if (transaction.err || transaction.warning) { if (transaction.err || transaction.warning) {
const { err, warning = {} } = transaction const { err, warning = {} } = transaction
const errFirst = !!((err && warning) || err) const errFirst = !!((err && warning) || err)
const message = errFirst ? err.message : warning.message
errFirst ? err.message : warning.message errFirst ? err.message : warning.message
// show error // show error
if (err) { if (err) {
const message = err.message || '' const message = err.message || ''
return ( return (
h(Tooltip, { h(Tooltip, {
title: message, title: message,
position: 'bottom', position: 'bottom',
}, [ }, [
h(`span.error`, ` (Failed)`), h(`span.error`, ` (Failed)`),
]) ])
) )
} }
// show warning // show warning
if (warning) { if (warning) {
const message = warning.message const message = warning.message
return h(Tooltip, { return h(Tooltip, {
title: message, title: message,
position: 'bottom', position: 'bottom',
}, [ }, [
h(`span.warning`, ` (Warning)`), h(`span.warning`, ` (Warning)`),
]) ])
}
} }
} }

View File

@ -6,10 +6,10 @@ const classnames = require('classnames')
const abi = require('human-standard-token-abi') const abi = require('human-standard-token-abi')
const abiDecoder = require('abi-decoder') const abiDecoder = require('abi-decoder')
abiDecoder.addABI(abi) abiDecoder.addABI(abi)
const prefixForNetwork = require('../../lib/etherscan-prefix-for-network')
const Identicon = require('./identicon') 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') const { getCurrentCurrency } = require('../selectors')
@ -19,12 +19,31 @@ function mapStateToProps (state) {
return { return {
tokens: state.metamask.tokens, tokens: state.metamask.tokens,
currentCurrency: getCurrentCurrency(state), currentCurrency: getCurrentCurrency(state),
tokenExchangeRates: state.metamask.tokenExchangeRates,
} }
} }
inherits(TxListItem, Component) inherits(TxListItem, Component)
function TxListItem () { function TxListItem () {
Component.call(this) 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 () { 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 { const {
txParams = {}, txParams = {},
tokens, conversionRate,
tokenExchangeRates,
currentCurrency,
} = this.props } = this.props
const toAddress = txParams.to
const decodedData = txParams.data && abiDecoder.decodeMethod(txParams.data) const decodedData = txParams.data && abiDecoder.decodeMethod(txParams.data)
const { params = [] } = decodedData || {} const { params = [] } = decodedData || {}
const { value } = params[1] || {} 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 multiplier = Math.pow(10, Number(decimals || 0))
const total = Number(value / multiplier) 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 { return {
total: `${total} ${symbol}`, total: `${total} ${symbol}`,
fiatTotal: showFiat && `${totalInFiat} ${currentCurrency.toUpperCase()}`,
} }
} }
@ -112,15 +175,8 @@ TxListItem.prototype.render = function () {
dateString, dateString,
address, address,
className, className,
txParams = {},
} = this.props } = this.props
const { total, fiatTotal } = this.state
const decodedData = txParams.data && abiDecoder.decodeMethod(txParams.data)
const { name: txDataName } = decodedData || {}
const { total, fiatTotal } = txDataName === 'transfer'
? this.getSendTokenTotal()
: this.getSendEtherTotal()
return h(`div${className || ''}`, { return h(`div${className || ''}`, {
key: transActionId, key: transActionId,
@ -182,10 +238,10 @@ TxListItem.prototype.render = function () {
}), }),
}, total), }, 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
]) ])
} }

View File

@ -6,9 +6,10 @@ const prefixForNetwork = require('../../lib/etherscan-prefix-for-network')
const selectors = require('../selectors') const selectors = require('../selectors')
const TxListItem = require('./tx-list-item') const TxListItem = require('./tx-list-item')
const ShiftListItem = require('./shift-list-item') const ShiftListItem = require('./shift-list-item')
const { formatBalance, formatDate } = require('../util') const { formatDate } = require('../util')
const { showConfTxPage } = require('../actions') const { showConfTxPage } = require('../actions')
const classnames = require('classnames') const classnames = require('classnames')
const { tokenInfoGetter } = require('../token-util')
module.exports = connect(mapStateToProps, mapDispatchToProps)(TxList) module.exports = connect(mapStateToProps, mapDispatchToProps)(TxList)
@ -21,7 +22,7 @@ function mapStateToProps (state) {
function mapDispatchToProps (dispatch) { function mapDispatchToProps (dispatch) {
return { return {
showConfTxPage: ({ id }) => dispatch(showConfTxPage({ id })) showConfTxPage: ({ id }) => dispatch(showConfTxPage({ id })),
} }
} }
@ -30,10 +31,11 @@ function TxList () {
Component.call(this) Component.call(this)
} }
TxList.prototype.componentWillMount = function () {
this.tokenInfoGetter = tokenInfoGetter()
}
TxList.prototype.render = function () { TxList.prototype.render = function () {
const { txsToRender, showConfTxPage } = this.props
return h('div.flex-column.tx-list-container', {}, [ return h('div.flex-column.tx-list-container', {}, [
h('div.flex-row.tx-list-header-wrapper', [ h('div.flex-row.tx-list-header-wrapper', [
@ -93,15 +95,15 @@ TxList.prototype.renderTransactionListItem = function (transaction, conversionRa
txParams: transaction.txParams, txParams: transaction.txParams,
transactionStatus, transactionStatus,
transActionId, transActionId,
key: transActionId,
dateString, dateString,
address, address,
transactionAmount, transactionAmount,
transactionHash, transactionHash,
conversionRate, conversionRate,
tokenInfoGetter: this.tokenInfoGetter,
} }
const isUnapproved = transactionStatus === 'unapproved'; const isUnapproved = transactionStatus === 'unapproved'
if (isUnapproved) { if (isUnapproved) {
opts.onClick = () => showConfTxPage({id: transActionId}) opts.onClick = () => showConfTxPage({id: transActionId})

View File

@ -114,14 +114,14 @@ ConfirmTxScreen.prototype.render = function () {
function currentTxView (opts) { function currentTxView (opts) {
log.info('rendering current tx view') log.info('rendering current tx view')
const { txData } = opts const { txData } = opts
const { txParams, msgParams, type } = txData const { txParams, msgParams } = txData
if (txParams) { if (txParams) {
log.debug('txParams detected, rendering pending tx') log.debug('txParams detected, rendering pending tx')
return h(PendingTx, opts) return h(PendingTx, opts)
} else if (msgParams) { } else if (msgParams) {
log.debug('msgParams detected, rendering pending msg') log.debug('msgParams detected, rendering pending msg')
return h(SignatureRequest, opts) return h(SignatureRequest, opts)
// if (type === 'eth_sign') { // if (type === 'eth_sign') {

View File

@ -13,7 +13,6 @@
* @param {string} [options.fromDenomination = 'WEI'] The denomination of the passed value * @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.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.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)} * @returns {(number | string | BN)}
* *
* The utility passes value along with the options as a single object to the `converter` function. * 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 BigNumber = require('bignumber.js')
const ethUtil = require('ethereumjs-util')
const BN = ethUtil.BN
const R = require('ramda') const R = require('ramda')
const { stripHexPrefix } = require('ethereumjs-util') const { stripHexPrefix } = require('ethereumjs-util')
@ -38,6 +39,7 @@ const BIG_NUMBER_GWEI_MULTIPLIER = new BigNumber('1000000000')
const convert = R.invoker(1, 'times') const convert = R.invoker(1, 'times')
const round = R.invoker(2, 'round')(R.__, BigNumber.ROUND_DOWN) const round = R.invoker(2, 'round')(R.__, BigNumber.ROUND_DOWN)
const invertConversionRate = conversionRate => () => new BigNumber(1.0).div(conversionRate) const invertConversionRate = conversionRate => () => new BigNumber(1.0).div(conversionRate)
const decToBigNumberViaString = n => R.pipe(String, toBigNumber['dec'])
// Setter Maps // Setter Maps
const toBigNumber = { const toBigNumber = {
@ -51,7 +53,7 @@ const toNormalizedDenomination = {
} }
const toSpecifiedDenomination = { const toSpecifiedDenomination = {
WEI: bigNumber => bigNumber.times(BIG_NUMBER_WEI_MULTIPLIER).round(), 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 = { const baseChange = {
hex: n => n.toString(16), hex: n => n.toString(16),
@ -95,16 +97,16 @@ const whenPropApplySetterMap = (prop, setterMap) => whenPredSetWithPropAndSetter
// Conversion utility function // Conversion utility function
const converter = R.pipe( const converter = R.pipe(
whenPredSetCRWithPropAndSetter(R.prop('conversionRate'), 'conversionRate', decToBigNumberViaString),
whenPredSetCRWithPropAndSetter(R.prop('invertConversionRate'), 'conversionRate', invertConversionRate), whenPredSetCRWithPropAndSetter(R.prop('invertConversionRate'), 'conversionRate', invertConversionRate),
whenPropApplySetterMap('fromNumericBase', toBigNumber), whenPropApplySetterMap('fromNumericBase', toBigNumber),
whenPropApplySetterMap('fromDenomination', toNormalizedDenomination), whenPropApplySetterMap('fromDenomination', toNormalizedDenomination),
whenPredSetWithPropAndSetter(fromAndToCurrencyPropsNotEqual, 'conversionRate', convert), whenPredSetWithPropAndSetter(fromAndToCurrencyPropsNotEqual, 'conversionRate', convert),
whenPropApplySetterMap('toDenomination', toSpecifiedDenomination), whenPropApplySetterMap('toDenomination', toSpecifiedDenomination),
whenPredSetWithPropAndSetter(R.prop('ethToUSDRate'), 'ethToUSDRate', convert),
whenPredSetWithPropAndSetter(R.prop('numberOfDecimals'), 'numberOfDecimals', round), whenPredSetWithPropAndSetter(R.prop('numberOfDecimals'), 'numberOfDecimals', round),
whenPropApplySetterMap('toNumericBase', baseChange), whenPropApplySetterMap('toNumericBase', baseChange),
R.view(R.lensProp('value')) R.view(R.lensProp('value'))
); )
const conversionUtil = (value, { const conversionUtil = (value, {
fromCurrency = null, fromCurrency = null,
@ -115,7 +117,6 @@ const conversionUtil = (value, {
toDenomination, toDenomination,
numberOfDecimals, numberOfDecimals,
conversionRate, conversionRate,
ethToUSDRate,
invertConversionRate, invertConversionRate,
}) => converter({ }) => converter({
fromCurrency, fromCurrency,
@ -126,18 +127,17 @@ const conversionUtil = (value, {
toDenomination, toDenomination,
numberOfDecimals, numberOfDecimals,
conversionRate, conversionRate,
ethToUSDRate,
invertConversionRate, invertConversionRate,
value: value || '0', value: value || '0',
}); })
const addCurrencies = (a, b, options = {}) => { const addCurrencies = (a, b, options = {}) => {
const { const {
aBase, aBase,
bBase, bBase,
...conversionOptions, ...conversionOptions
} = options } = options
const value = (new BigNumber(a, aBase)).add(b, bBase); const value = (new BigNumber(a, aBase)).add(b, bBase)
return converter({ return converter({
value, value,
@ -149,10 +149,13 @@ const multiplyCurrencies = (a, b, options = {}) => {
const { const {
multiplicandBase, multiplicandBase,
multiplierBase, multiplierBase,
...conversionOptions, ...conversionOptions
} = options } = 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({ return converter({
value, value,
@ -162,16 +165,41 @@ const multiplyCurrencies = (a, b, options = {}) => {
const conversionGreaterThan = ( const conversionGreaterThan = (
{ ...firstProps }, { ...firstProps },
{ ...secondProps }, { ...secondProps },
) => { ) => {
const firstValue = converter({ ...firstProps }) const firstValue = converter({ ...firstProps })
const secondValue = converter({ ...secondProps }) const secondValue = converter({ ...secondProps })
return firstValue.gt(secondValue) 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 = { module.exports = {
conversionUtil, conversionUtil,
addCurrencies, addCurrencies,
multiplyCurrencies, multiplyCurrencies,
conversionGreaterThan, conversionGreaterThan,
} conversionGTE,
conversionLTE,
toNegative,
}

View File

@ -15,6 +15,7 @@
width: 250px; width: 250px;
font-size: 14px; font-size: 14px;
text-align: center; text-align: center;
border: 1px solid $alto;
&--error { &--error {
border: 1px solid $monzo; border: 1px solid $monzo;

View File

@ -131,8 +131,8 @@
margin-top: 20px; margin-top: 20px;
} }
&__notice { &__notice,
color: #9B9B9B; &__warning {
font-family: "Avenir Next"; font-family: "Avenir Next";
font-size: 14px; font-size: 14px;
line-height: 19px; line-height: 19px;
@ -142,6 +142,14 @@
width: 100%; width: 100%;
} }
&__notice {
color: $dusty-gray;
}
&__warning {
color: $crimson;
}
&__rows { &__rows {
height: 100%; height: 100%;
overflow-y: scroll; overflow-y: scroll;

View File

@ -37,11 +37,7 @@ MainContainer.prototype.render = function () {
break break
case 'config': case 'config':
log.debug('rendering config screen from unlock screen.') log.debug('rendering config screen from unlock screen.')
contents = { return h(Settings, {key: 'config'})
component: Settings,
key: 'config',
}
break
default: default:
log.debug('rendering locked screen') log.debug('rendering locked screen')
contents = { contents = {

View File

@ -44,7 +44,7 @@ function reduceApp (state, action) {
}, },
previousModalState: { previousModalState: {
name: null, name: null,
} },
}, },
sidebarOpen: false, sidebarOpen: false,
networkDropdownOpen: false, networkDropdownOpen: false,
@ -100,7 +100,7 @@ function reduceApp (state, action) {
state.appState.modal, state.appState.modal,
{ open: false }, { open: false },
{ modalState: { name: null } }, { modalState: { name: null } },
{ previousModalState: appState.modal.modalState}, { previousModalState: appState.modal.modalState},
), ),
}) })

View File

@ -235,7 +235,7 @@ function reduceMetamask (state, action) {
errors: { errors: {
...metamaskState.send.errors, ...metamaskState.send.errors,
...action.value, ...action.value,
} },
}, },
}) })

View File

@ -1,8 +1,6 @@
const { inherits } = require('util') const { inherits } = require('util')
const PersistentForm = require('../lib/persistent-form') const PersistentForm = require('../lib/persistent-form')
const h = require('react-hyperscript') const h = require('react-hyperscript')
const connect = require('react-redux').connect
const classnames = require('classnames')
const Identicon = require('./components/identicon') const Identicon = require('./components/identicon')
const FromDropdown = require('./components/send/from-dropdown') 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 { MIN_GAS_TOTAL } = require('./components/send/send-constants')
const { showModal } = require('./actions')
const { const {
multiplyCurrencies, multiplyCurrencies,
conversionGreaterThan, conversionGreaterThan,
addCurrencies,
} = require('./conversion-util') } = require('./conversion-util')
const { const {
isBalanceSufficient, isBalanceSufficient,
@ -99,7 +94,7 @@ SendTransactionScreen.prototype.renderHeaderIcon = function () {
diameter: 40, diameter: 40,
address: selectedToken.address, 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 { errors } = this.props
const errorMessage = errors[errorType]; const errorMessage = errors[errorType]
return errorMessage return errorMessage
? h('div.send-v2__error', [ errorMessage ] ) ? h('div.send-v2__error', [ errorMessage ])
: null : null
} }
@ -154,7 +149,6 @@ SendTransactionScreen.prototype.renderFromRow = function () {
from, from,
fromAccounts, fromAccounts,
conversionRate, conversionRate,
setSelectedAddress,
updateSendFrom, updateSendFrom,
} = this.props } = this.props
@ -243,7 +237,6 @@ SendTransactionScreen.prototype.validateAmount = function (value) {
amountConversionRate, amountConversionRate,
conversionRate, conversionRate,
primaryCurrency, primaryCurrency,
toCurrency,
selectedToken, selectedToken,
gasTotal, gasTotal,
} = this.props } = this.props
@ -440,7 +433,6 @@ SendTransactionScreen.prototype.onSubmit = function (event) {
signTokenTx, signTokenTx,
signTx, signTx,
selectedToken, selectedToken,
toAccounts,
clearSend, clearSend,
errors: { amount: amountError, to: toError }, errors: { amount: amountError, to: toError },
} = this.props } = this.props

File diff suppressed because it is too large Load Diff

36
ui/app/token-util.js Normal file
View File

@ -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,
}