aboutsummaryrefslogtreecommitdiffstats
path: root/ui/app
diff options
context:
space:
mode:
Diffstat (limited to 'ui/app')
-rw-r--r--ui/app/actions.js122
-rw-r--r--ui/app/app.js19
-rw-r--r--ui/app/components/buy-button-subview.js2
-rw-r--r--ui/app/components/customize-gas-modal/index.js4
-rw-r--r--ui/app/components/export-text-container/export-text-container.component.js45
-rw-r--r--ui/app/components/export-text-container/export-text-container.scss52
-rw-r--r--ui/app/components/export-text-container/index.js2
-rw-r--r--ui/app/components/loading-screen/index.js2
-rw-r--r--ui/app/components/loading-screen/loading-screen.component.js (renamed from ui/app/components/loading.js)13
-rw-r--r--ui/app/components/pages/add-token.js2
-rw-r--r--ui/app/components/pages/create-account/import-account/json.js2
-rw-r--r--ui/app/components/pages/create-account/import-account/private-key.js2
-rw-r--r--ui/app/components/pages/home.js4
-rw-r--r--ui/app/components/pages/keychains/reveal-seed.js259
-rw-r--r--ui/app/components/pending-tx/index.js40
-rw-r--r--ui/app/components/send/currency-display.js15
-rw-r--r--ui/app/components/send/send-constants.js4
-rw-r--r--ui/app/components/shapeshift-form.js13
-rw-r--r--ui/app/components/spinner/index.js2
-rw-r--r--ui/app/components/spinner/spinner.component.js78
-rw-r--r--ui/app/conf-tx.js2
-rw-r--r--ui/app/css/itcss/components/index.scss2
-rw-r--r--ui/app/css/itcss/components/loading-overlay.scss21
-rw-r--r--ui/app/css/itcss/components/pages/index.scss2
-rw-r--r--ui/app/css/itcss/components/pages/reveal-seed.scss17
-rw-r--r--ui/app/css/itcss/generic/index.scss67
-rw-r--r--ui/app/css/itcss/settings/variables.scss1
-rw-r--r--ui/app/keychains/hd/recover-seed/confirmation.js138
-rw-r--r--ui/app/send-v2.js2
-rw-r--r--ui/app/token-util.js46
30 files changed, 554 insertions, 426 deletions
diff --git a/ui/app/actions.js b/ui/app/actions.js
index 81d9c333b..f060e40bd 100644
--- a/ui/app/actions.js
+++ b/ui/app/actions.js
@@ -1,4 +1,5 @@
const abi = require('human-standard-token-abi')
+const pify = require('pify')
const getBuyEthUrl = require('../../app/scripts/lib/buy-eth-url')
const { getTokenAddressFromTokenObject } = require('./util')
const ethUtil = require('ethereumjs-util')
@@ -83,7 +84,7 @@ var actions = {
REVEAL_SEED_CONFIRMATION: 'REVEAL_SEED_CONFIRMATION',
revealSeedConfirmation: revealSeedConfirmation,
requestRevealSeed: requestRevealSeed,
-
+ requestRevealSeedWords,
// unlock screen
UNLOCK_IN_PROGRESS: 'UNLOCK_IN_PROGRESS',
UNLOCK_FAILED: 'UNLOCK_FAILED',
@@ -345,11 +346,13 @@ function transitionBackward () {
}
}
-function clearSeedWordCache () {
- log.debug(`background.clearSeedWordCache`)
+function confirmSeedWords () {
return dispatch => {
+ dispatch(actions.showLoadingIndication())
+ log.debug(`background.clearSeedWordCache`)
return new Promise((resolve, reject) => {
background.clearSeedWordCache((err, account) => {
+ dispatch(actions.hideLoadingIndication())
if (err) {
dispatch(actions.displayWarning(err.message))
return reject(err)
@@ -363,22 +366,6 @@ function clearSeedWordCache () {
}
}
-function confirmSeedWords () {
- return async dispatch => {
- dispatch(actions.showLoadingIndication())
- const account = await dispatch(clearSeedWordCache())
- return dispatch(setIsRevealingSeedWords(false))
- .then(() => {
- dispatch(actions.hideLoadingIndication())
- return account
- })
- .catch(() => {
- dispatch(actions.hideLoadingIndication())
- return account
- })
- }
-}
-
function createNewVaultAndRestore (password, seed) {
return (dispatch) => {
dispatch(actions.showLoadingIndication())
@@ -441,6 +428,30 @@ function revealSeedConfirmation () {
}
}
+function verifyPassword (password) {
+ return new Promise((resolve, reject) => {
+ background.submitPassword(password, error => {
+ if (error) {
+ return reject(error)
+ }
+
+ resolve(true)
+ })
+ })
+}
+
+function verifySeedPhrase () {
+ return new Promise((resolve, reject) => {
+ background.verifySeedPhrase((error, seedWords) => {
+ if (error) {
+ return reject(error)
+ }
+
+ resolve(seedWords)
+ })
+ })
+}
+
function requestRevealSeed (password) {
return dispatch => {
dispatch(actions.showLoadingIndication())
@@ -460,13 +471,29 @@ function requestRevealSeed (password) {
}
dispatch(actions.showNewVaultSeed(result))
+ dispatch(actions.hideLoadingIndication())
resolve()
})
})
})
- .then(() => dispatch(setIsRevealingSeedWords(true)))
- .then(() => dispatch(actions.hideLoadingIndication()))
- .catch(() => dispatch(actions.hideLoadingIndication()))
+ }
+}
+
+function requestRevealSeedWords (password) {
+ return async dispatch => {
+ dispatch(actions.showLoadingIndication())
+ log.debug(`background.submitPassword`)
+
+ try {
+ await verifyPassword(password)
+ const seedWords = await verifySeedPhrase()
+ dispatch(actions.hideLoadingIndication())
+ return seedWords
+ } catch (error) {
+ dispatch(actions.hideLoadingIndication())
+ dispatch(actions.displayWarning(error.message))
+ throw new Error(error.message)
+ }
}
}
@@ -497,31 +524,26 @@ function addNewKeyring (type, opts) {
}
function importNewAccount (strategy, args) {
- return (dispatch) => {
- dispatch(actions.showLoadingIndication('This may take a while, be patient.'))
- log.debug(`background.importAccountWithStrategy`)
- return new Promise((resolve, reject) => {
- background.importAccountWithStrategy(strategy, args, (err) => {
- if (err) {
- dispatch(actions.displayWarning(err.message))
- return reject(err)
- }
- log.debug(`background.getState`)
- background.getState((err, newState) => {
- dispatch(actions.hideLoadingIndication())
- if (err) {
- dispatch(actions.displayWarning(err.message))
- return reject(err)
- }
- dispatch(actions.updateMetamaskState(newState))
- dispatch({
- type: actions.SHOW_ACCOUNT_DETAIL,
- value: newState.selectedAddress,
- })
- resolve(newState)
- })
- })
+ return async (dispatch) => {
+ let newState
+ dispatch(actions.showLoadingIndication('This may take a while, please be patient.'))
+ try {
+ log.debug(`background.importAccountWithStrategy`)
+ await pify(background.importAccountWithStrategy).call(background, strategy, args)
+ log.debug(`background.getState`)
+ newState = await pify(background.getState).call(background)
+ } catch (err) {
+ dispatch(actions.hideLoadingIndication())
+ dispatch(actions.displayWarning(err.message))
+ throw err
+ }
+ dispatch(actions.hideLoadingIndication())
+ dispatch(actions.updateMetamaskState(newState))
+ dispatch({
+ type: actions.SHOW_ACCOUNT_DETAIL,
+ value: newState.selectedAddress,
})
+ return newState
}
}
@@ -1923,11 +1945,3 @@ function updateNetworkEndpointType (networkEndpointType) {
value: networkEndpointType,
}
}
-
-function setIsRevealingSeedWords (reveal) {
- return dispatch => {
- log.debug(`background.setIsRevealingSeedWords`)
- background.setIsRevealingSeedWords(reveal)
- return forceUpdateMetamaskState(dispatch)
- }
-}
diff --git a/ui/app/app.js b/ui/app/app.js
index 0b38b1326..c93a6314c 100644
--- a/ui/app/app.js
+++ b/ui/app/app.js
@@ -24,12 +24,12 @@ const Initialized = require('./components/pages/initialized')
const Settings = require('./components/pages/settings')
const UnlockPage = require('./components/pages/unlock')
const RestoreVaultPage = require('./components/pages/keychains/restore-vault')
-const RevealSeedConfirmation = require('./keychains/hd/recover-seed/confirmation')
+const RevealSeedConfirmation = require('./components/pages/keychains/reveal-seed')
const AddTokenPage = require('./components/pages/add-token')
const CreateAccountPage = require('./components/pages/create-account')
const NoticeScreen = require('./components/pages/notice')
-const Loading = require('./components/loading')
+const Loading = require('./components/loading-screen')
const NetworkIndicator = require('./components/network')
const Identicon = require('./components/identicon')
const ReactCSSTransitionGroup = require('react-addons-css-transition-group')
@@ -56,20 +56,11 @@ const {
class App extends Component {
componentWillMount () {
- const {
- currentCurrency,
- setCurrentCurrencyToUSD,
- isRevealingSeedWords,
- clearSeedWords,
- } = this.props
+ const { currentCurrency, setCurrentCurrencyToUSD } = this.props
if (!currentCurrency) {
setCurrentCurrencyToUSD()
}
-
- if (isRevealingSeedWords) {
- clearSeedWords()
- }
}
renderRoutes () {
@@ -144,6 +135,7 @@ class App extends Component {
(isLoading || isLoadingNetwork) && h(Loading, {
loadingMessage: loadMessage,
+ fullScreen: true,
}),
// content
@@ -402,8 +394,6 @@ App.propTypes = {
isMouseUser: PropTypes.bool,
setMouseUserState: PropTypes.func,
t: PropTypes.func,
- isRevealingSeedWords: PropTypes.bool,
- clearSeedWords: PropTypes.func,
}
function mapStateToProps (state) {
@@ -484,7 +474,6 @@ function mapDispatchToProps (dispatch, ownProps) {
setCurrentCurrencyToUSD: () => dispatch(actions.setCurrentCurrency('usd')),
toggleAccountMenu: () => dispatch(actions.toggleAccountMenu()),
setMouseUserState: (isMouseUser) => dispatch(actions.setMouseUserState(isMouseUser)),
- clearSeedWords: () => dispatch(actions.confirmSeedWords()),
}
}
diff --git a/ui/app/components/buy-button-subview.js b/ui/app/components/buy-button-subview.js
index fda7c3e17..c6957d2aa 100644
--- a/ui/app/components/buy-button-subview.js
+++ b/ui/app/components/buy-button-subview.js
@@ -6,7 +6,7 @@ const connect = require('react-redux').connect
const actions = require('../actions')
const CoinbaseForm = require('./coinbase-form')
const ShapeshiftForm = require('./shapeshift-form')
-const Loading = require('./loading')
+const Loading = require('./loading-screen')
const AccountPanel = require('./account-panel')
const RadioList = require('./custom-radio-list')
const { getNetworkDisplayName } = require('../../../app/scripts/controllers/network/util')
diff --git a/ui/app/components/customize-gas-modal/index.js b/ui/app/components/customize-gas-modal/index.js
index 4c693d1c3..1ff8eea87 100644
--- a/ui/app/components/customize-gas-modal/index.js
+++ b/ui/app/components/customize-gas-modal/index.js
@@ -280,8 +280,7 @@ CustomizeGasModal.prototype.render = function () {
h(GasModalCard, {
value: convertedGasPrice,
min: forceGasMin || MIN_GAS_PRICE_GWEI,
- // max: 1000,
- step: multiplyCurrencies(MIN_GAS_PRICE_GWEI, 10),
+ step: 1,
onChange: value => this.convertAndSetGasPrice(value),
title: this.context.t('gasPrice'),
copy: this.context.t('gasPriceCalculation'),
@@ -290,7 +289,6 @@ CustomizeGasModal.prototype.render = function () {
h(GasModalCard, {
value: convertedGasLimit,
min: 1,
- // max: 100000,
step: 1,
onChange: value => this.convertAndSetGasLimit(value),
title: this.context.t('gasLimit'),
diff --git a/ui/app/components/export-text-container/export-text-container.component.js b/ui/app/components/export-text-container/export-text-container.component.js
new file mode 100644
index 000000000..c2546fa9b
--- /dev/null
+++ b/ui/app/components/export-text-container/export-text-container.component.js
@@ -0,0 +1,45 @@
+const { Component } = require('react')
+const PropTypes = require('prop-types')
+const h = require('react-hyperscript')
+const copyToClipboard = require('copy-to-clipboard')
+const { exportAsFile } = require('../../util')
+
+class ExportTextContainer extends Component {
+ render () {
+ const { text = '', filename = '' } = this.props
+ const { t } = this.context
+
+ return (
+ h('.export-text-container', [
+ h('.export-text-container__text-container', [
+ h('.export-text-container__text', text),
+ ]),
+ h('.export-text-container__buttons-container', [
+ h('.export-text-container__button.export-text-container__button--copy', {
+ onClick: () => copyToClipboard(text),
+ }, [
+ h('img', { src: 'images/copy-to-clipboard.svg' }),
+ h('.export-text-container__button-text', t('copyToClipboard')),
+ ]),
+ h('.export-text-container__button', {
+ onClick: () => exportAsFile(filename, text),
+ }, [
+ h('img', { src: 'images/download.svg' }),
+ h('.export-text-container__button-text', t('saveAsCsvFile')),
+ ]),
+ ]),
+ ])
+ )
+ }
+}
+
+ExportTextContainer.propTypes = {
+ text: PropTypes.string,
+ filename: PropTypes.string,
+}
+
+ExportTextContainer.contextTypes = {
+ t: PropTypes.func,
+}
+
+module.exports = ExportTextContainer
diff --git a/ui/app/components/export-text-container/export-text-container.scss b/ui/app/components/export-text-container/export-text-container.scss
new file mode 100644
index 000000000..a42de8233
--- /dev/null
+++ b/ui/app/components/export-text-container/export-text-container.scss
@@ -0,0 +1,52 @@
+.export-text-container {
+ display: flex;
+ justify-content: center;
+ flex-direction: column;
+ align-items: center;
+ border: 1px solid $alto;
+ border-radius: 4px;
+ font-weight: 400;
+
+ &__text-container {
+ width: 100%;
+ display: flex;
+ justify-content: center;
+ padding: 20px;
+ border-radius: 4px;
+ background: $alabaster;
+ }
+
+ &__text {
+ resize: none;
+ border: none;
+ background: $alabaster;
+ font-size: 20px;
+ text-align: center;
+ }
+
+ &__buttons-container {
+ display: flex;
+ flex-direction: row;
+ border-top: 1px solid $alto;
+ width: 100%;
+ }
+
+ &__button {
+ padding: 10px;
+ flex: 1;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ font-size: 14px;
+ cursor: pointer;
+ color: $curious-blue;
+
+ &--copy {
+ border-right: 1px solid $alto;
+ }
+ }
+
+ &__button-text {
+ padding-left: 10px;
+ }
+}
diff --git a/ui/app/components/export-text-container/index.js b/ui/app/components/export-text-container/index.js
new file mode 100644
index 000000000..b2864a717
--- /dev/null
+++ b/ui/app/components/export-text-container/index.js
@@ -0,0 +1,2 @@
+const ExportTextContainer = require('./export-text-container.component')
+module.exports = ExportTextContainer
diff --git a/ui/app/components/loading-screen/index.js b/ui/app/components/loading-screen/index.js
new file mode 100644
index 000000000..191d953f7
--- /dev/null
+++ b/ui/app/components/loading-screen/index.js
@@ -0,0 +1,2 @@
+const LoadingScreen = require('./loading-screen.component')
+module.exports = LoadingScreen
diff --git a/ui/app/components/loading.js b/ui/app/components/loading-screen/loading-screen.component.js
index b9afc550f..bce2a4aac 100644
--- a/ui/app/components/loading.js
+++ b/ui/app/components/loading-screen/loading-screen.component.js
@@ -2,8 +2,9 @@ const { Component } = require('react')
const h = require('react-hyperscript')
const PropTypes = require('prop-types')
const classnames = require('classnames')
+const Spinner = require('../spinner')
-class LoadingIndicator extends Component {
+class LoadingScreen extends Component {
renderMessage () {
const { loadingMessage } = this.props
return loadingMessage && h('span', loadingMessage)
@@ -14,9 +15,9 @@ class LoadingIndicator extends Component {
h('.loading-overlay', {
className: classnames({ 'loading-overlay--full-screen': this.props.fullScreen }),
}, [
- h('.flex-center.flex-column', [
- h('img', {
- src: 'images/loading.svg',
+ h('.loading-overlay__container', [
+ h(Spinner, {
+ color: '#F7C06C',
}),
this.renderMessage(),
@@ -26,9 +27,9 @@ class LoadingIndicator extends Component {
}
}
-LoadingIndicator.propTypes = {
+LoadingScreen.propTypes = {
loadingMessage: PropTypes.string,
fullScreen: PropTypes.bool,
}
-module.exports = LoadingIndicator
+module.exports = LoadingScreen
diff --git a/ui/app/components/pages/add-token.js b/ui/app/components/pages/add-token.js
index 566e42450..8d52571d0 100644
--- a/ui/app/components/pages/add-token.js
+++ b/ui/app/components/pages/add-token.js
@@ -192,7 +192,7 @@ AddTokenScreen.prototype.attemptToAutoFillTokenParams = async function (address)
if (symbol && decimals) {
this.setState({
customSymbol: symbol,
- customDecimals: decimals.toString(),
+ customDecimals: decimals,
autoFilled: true,
})
}
diff --git a/ui/app/components/pages/create-account/import-account/json.js b/ui/app/components/pages/create-account/import-account/json.js
index 946907a47..0a3314b2a 100644
--- a/ui/app/components/pages/create-account/import-account/json.js
+++ b/ui/app/components/pages/create-account/import-account/json.js
@@ -105,6 +105,8 @@ class JsonImportSubview extends Component {
}
this.props.importNewJsonAccount([ fileContents, password ])
+ // JS runtime requires caught rejections but failures are handled by Redux
+ .catch()
}
}
diff --git a/ui/app/components/pages/create-account/import-account/private-key.js b/ui/app/components/pages/create-account/import-account/private-key.js
index c77612ea4..df7ac910a 100644
--- a/ui/app/components/pages/create-account/import-account/private-key.js
+++ b/ui/app/components/pages/create-account/import-account/private-key.js
@@ -91,5 +91,7 @@ PrivateKeyImportView.prototype.createNewKeychain = function () {
const { importNewAccount, history } = this.props
importNewAccount('Private Key', [ privateKey ])
+ // JS runtime requires caught rejections but failures are handled by Redux
+ .catch()
.then(() => history.push(DEFAULT_ROUTE))
}
diff --git a/ui/app/components/pages/home.js b/ui/app/components/pages/home.js
index 90b8e1d37..9110f8202 100644
--- a/ui/app/components/pages/home.js
+++ b/ui/app/components/pages/home.js
@@ -21,7 +21,7 @@ const QrView = require('../../components/qr-code')
// Routes
const {
- REVEAL_SEED_ROUTE,
+ INITIALIZE_BACKUP_PHRASE_ROUTE,
RESTORE_VAULT_ROUTE,
CONFIRM_TRANSACTION_ROUTE,
NOTICE_ROUTE,
@@ -69,7 +69,7 @@ class Home extends Component {
log.debug('rendering seed words')
return h(Redirect, {
to: {
- pathname: REVEAL_SEED_ROUTE,
+ pathname: INITIALIZE_BACKUP_PHRASE_ROUTE,
},
})
}
diff --git a/ui/app/components/pages/keychains/reveal-seed.js b/ui/app/components/pages/keychains/reveal-seed.js
index 247f3c8e2..685c81074 100644
--- a/ui/app/components/pages/keychains/reveal-seed.js
+++ b/ui/app/components/pages/keychains/reveal-seed.js
@@ -2,11 +2,27 @@ const { Component } = require('react')
const { connect } = require('react-redux')
const PropTypes = require('prop-types')
const h = require('react-hyperscript')
-const { exportAsFile } = require('../../../util')
-const { requestRevealSeed, confirmSeedWords } = require('../../../actions')
+const classnames = require('classnames')
+
+const { requestRevealSeedWords } = require('../../../actions')
const { DEFAULT_ROUTE } = require('../../../routes')
+const ExportTextContainer = require('../../export-text-container')
+
+const PASSWORD_PROMPT_SCREEN = 'PASSWORD_PROMPT_SCREEN'
+const REVEAL_SEED_SCREEN = 'REVEAL_SEED_SCREEN'
class RevealSeedPage extends Component {
+ constructor (props) {
+ super(props)
+
+ this.state = {
+ screen: PASSWORD_PROMPT_SCREEN,
+ password: '',
+ seedWords: null,
+ error: null,
+ }
+ }
+
componentDidMount () {
const passwordBox = document.getElementById('password-box')
if (passwordBox) {
@@ -14,182 +30,135 @@ class RevealSeedPage extends Component {
}
}
- checkConfirmation (event) {
- if (event.key === 'Enter') {
- event.preventDefault()
- this.revealSeedWords()
- }
+ handleSubmit (event) {
+ event.preventDefault()
+ this.setState({ seedWords: null, error: null })
+ this.props.requestRevealSeedWords(this.state.password)
+ .then(seedWords => this.setState({ seedWords, screen: REVEAL_SEED_SCREEN }))
+ .catch(error => this.setState({ error: error.message }))
}
- revealSeedWords () {
- const password = document.getElementById('password-box').value
- this.props.requestRevealSeed(password)
- }
-
- renderSeed () {
- const { seedWords, confirmSeedWords, history } = this.props
-
+ renderWarning () {
return (
- h('.initialize-screen.flex-column.flex-center.flex-grow', [
-
- h('h3.flex-center.text-transform-uppercase', {
- style: {
- background: '#EBEBEB',
- color: '#AEAEAE',
- marginTop: 36,
- marginBottom: 8,
- width: '100%',
- fontSize: '20px',
- padding: 6,
- },
- }, [
- 'Vault Created',
- ]),
-
- h('div', {
- style: {
- fontSize: '1em',
- marginTop: '10px',
- textAlign: 'center',
- },
- }, [
- h('span.error', 'These 12 words are the only way to restore your MetaMask accounts.\nSave them somewhere safe and secret.'),
- ]),
-
- h('textarea.twelve-word-phrase', {
- readOnly: true,
- value: seedWords,
+ h('.page-container__warning-container', [
+ h('img.page-container__warning-icon', {
+ src: 'images/warning.svg',
}),
-
- h('button.primary', {
- onClick: () => confirmSeedWords().then(() => history.push(DEFAULT_ROUTE)),
- style: {
- margin: '24px',
- fontSize: '0.9em',
- marginBottom: '10px',
- },
- }, 'I\'ve copied it somewhere safe'),
-
- h('button.primary', {
- onClick: () => exportAsFile(`MetaMask Seed Words`, seedWords),
- style: {
- margin: '10px',
- fontSize: '0.9em',
- },
- }, 'Save Seed Words As File'),
+ h('.page-container__warning-message', [
+ h('.page-container__warning-title', [this.context.t('revealSeedWordsWarningTitle')]),
+ h('div', [this.context.t('revealSeedWordsWarning')]),
+ ]),
])
)
}
- renderConfirmation () {
- const { history, warning, inProgress } = this.props
+ renderContent () {
+ return this.state.screen === PASSWORD_PROMPT_SCREEN
+ ? this.renderPasswordPromptContent()
+ : this.renderRevealSeedContent()
+ }
+
+ renderPasswordPromptContent () {
+ const { t } = this.context
return (
- h('.initialize-screen.flex-column.flex-center.flex-grow', {
- style: { maxWidth: '420px' },
+ h('form', {
+ onSubmit: event => this.handleSubmit(event),
}, [
-
- h('h3.flex-center.text-transform-uppercase', {
- style: {
- background: '#EBEBEB',
- color: '#AEAEAE',
- marginBottom: 24,
- width: '100%',
- fontSize: '20px',
- padding: 6,
- },
- }, [
- 'Reveal Seed Words',
- ]),
-
- h('.div', {
- style: {
- display: 'flex',
- flexDirection: 'column',
- padding: '20px',
- justifyContent: 'center',
- },
- }, [
-
- h('h4', 'Do not recover your seed words in a public place! These words can be used to steal all your accounts.'),
-
- // confirmation
- h('input.large-input.letter-spacey', {
+ h('label.input-label', {
+ htmlFor: 'password-box',
+ }, t('enterPasswordContinue')),
+ h('.input-group', [
+ h('input.form-control', {
type: 'password',
+ placeholder: t('password'),
id: 'password-box',
- placeholder: 'Enter your password to confirm',
- onKeyPress: this.checkConfirmation.bind(this),
- style: {
- width: 260,
- marginTop: '12px',
- },
+ value: this.state.password,
+ onChange: event => this.setState({ password: event.target.value }),
+ className: classnames({ 'form-control--error': this.state.error }),
}),
+ ]),
+ this.state.error && h('.reveal-seed__error', this.state.error),
+ ])
+ )
+ }
- h('.flex-row.flex-start', {
- style: {
- marginTop: 30,
- width: '50%',
- },
- }, [
- // cancel
- h('button.primary', {
- onClick: () => history.push(DEFAULT_ROUTE),
- }, 'CANCEL'),
-
- // submit
- h('button.primary', {
- style: { marginLeft: '10px' },
- onClick: this.revealSeedWords.bind(this),
- }, 'OK'),
+ renderRevealSeedContent () {
+ const { t } = this.context
- ]),
+ return (
+ h('div', [
+ h('label.reveal-seed__label', t('yourPrivateSeedPhrase')),
+ h(ExportTextContainer, {
+ text: this.state.seedWords,
+ filename: t('metamaskSeedWords'),
+ }),
+ ])
+ )
+ }
- warning && (
- h('span.error', {
- style: {
- margin: '20px',
- },
- }, warning.split('-'))
- ),
-
- inProgress && (
- h('span.in-progress-notification', 'Generating Seed...')
- ),
- ]),
+ renderFooter () {
+ return this.state.screen === PASSWORD_PROMPT_SCREEN
+ ? this.renderPasswordPromptFooter()
+ : this.renderRevealSeedFooter()
+ }
+
+ renderPasswordPromptFooter () {
+ return (
+ h('.page-container__footer', [
+ h('button.btn-secondary--lg.page-container__footer-button', {
+ onClick: () => this.props.history.push(DEFAULT_ROUTE),
+ }, this.context.t('cancel')),
+ h('button.btn-primary--lg.page-container__footer-button', {
+ onClick: event => this.handleSubmit(event),
+ disabled: this.state.password === '',
+ }, this.context.t('next')),
+ ])
+ )
+ }
+
+ renderRevealSeedFooter () {
+ return (
+ h('.page-container__footer', [
+ h('button.btn-secondary--lg.page-container__footer-button', {
+ onClick: () => this.props.history.push(DEFAULT_ROUTE),
+ }, this.context.t('close')),
])
)
}
render () {
- return this.props.seedWords
- ? this.renderSeed()
- : this.renderConfirmation()
+ return (
+ h('.page-container', [
+ h('.page-container__header', [
+ h('.page-container__title', this.context.t('revealSeedWordsTitle')),
+ h('.page-container__subtitle', this.context.t('revealSeedWordsDescription')),
+ ]),
+ h('.page-container__content', [
+ this.renderWarning(),
+ h('.reveal-seed__content', [
+ this.renderContent(),
+ ]),
+ ]),
+ this.renderFooter(),
+ ])
+ )
}
}
RevealSeedPage.propTypes = {
- requestRevealSeed: PropTypes.func,
- confirmSeedWords: PropTypes.func,
- seedWords: PropTypes.string,
- inProgress: PropTypes.bool,
+ requestRevealSeedWords: PropTypes.func,
history: PropTypes.object,
- warning: PropTypes.string,
}
-const mapStateToProps = state => {
- const { appState: { warning }, metamask: { seedWords } } = state
-
- return {
- warning,
- seedWords,
- }
+RevealSeedPage.contextTypes = {
+ t: PropTypes.func,
}
const mapDispatchToProps = dispatch => {
return {
- requestRevealSeed: password => dispatch(requestRevealSeed(password)),
- confirmSeedWords: () => dispatch(confirmSeedWords()),
+ requestRevealSeedWords: password => dispatch(requestRevealSeedWords(password)),
}
}
-module.exports = connect(mapStateToProps, mapDispatchToProps)(RevealSeedPage)
+module.exports = connect(null, mapDispatchToProps)(RevealSeedPage)
diff --git a/ui/app/components/pending-tx/index.js b/ui/app/components/pending-tx/index.js
index 6ee83ba7e..893538bcf 100644
--- a/ui/app/components/pending-tx/index.js
+++ b/ui/app/components/pending-tx/index.js
@@ -8,11 +8,11 @@ const abiDecoder = require('abi-decoder')
abiDecoder.addABI(abi)
const inherits = require('util').inherits
const actions = require('../../actions')
-const util = require('../../util')
+const { getSymbolAndDecimals } = require('../../token-util')
const ConfirmSendEther = require('./confirm-send-ether')
const ConfirmSendToken = require('./confirm-send-token')
const ConfirmDeployContract = require('./confirm-deploy-contract')
-const Loading = require('../loading')
+const Loading = require('../loading-screen')
const TX_TYPES = {
DEPLOY_CONTRACT: 'deploy_contract',
@@ -26,6 +26,7 @@ function mapStateToProps (state) {
const {
conversionRate,
identities,
+ tokens: existingTokens,
} = state.metamask
const accounts = state.metamask.accounts
const selectedAddress = state.metamask.selectedAddress || Object.keys(accounts)[0]
@@ -33,6 +34,7 @@ function mapStateToProps (state) {
conversionRate,
identities,
selectedAddress,
+ existingTokens,
}
}
@@ -66,6 +68,7 @@ PendingTx.prototype.componentDidUpdate = function (prevProps, prevState) {
}
PendingTx.prototype.setTokenData = async function () {
+ const { existingTokens } = this.props
const txMeta = this.gatherTxMeta()
const txParams = txMeta.txParams || {}
@@ -89,30 +92,15 @@ PendingTx.prototype.setTokenData = async function () {
}
if (isTokenTransaction) {
- const token = util.getContractAtAddress(txParams.to)
- const results = await Promise.all([
- token.symbol(),
- token.decimals(),
- ])
- const [ symbol, decimals ] = results
-
- if (symbol[0] && decimals[0]) {
- this.setState({
- transactionType: TX_TYPES.SEND_TOKEN,
- tokenAddress: txParams.to,
- tokenSymbol: symbol[0],
- tokenDecimals: decimals[0],
- isFetching: false,
- })
- } else {
- this.setState({
- transactionType: TX_TYPES.SEND_TOKEN,
- tokenAddress: txParams.to,
- tokenSymbol: null,
- tokenDecimals: null,
- isFetching: false,
- })
- }
+ const { symbol, decimals } = await getSymbolAndDecimals(txParams.to, existingTokens)
+
+ this.setState({
+ transactionType: TX_TYPES.SEND_TOKEN,
+ tokenAddress: txParams.to,
+ tokenSymbol: symbol,
+ tokenDecimals: decimals,
+ isFetching: false,
+ })
} else {
this.setState({
transactionType: TX_TYPES.SEND_ETHER,
diff --git a/ui/app/components/send/currency-display.js b/ui/app/components/send/currency-display.js
index a7bd5d7ea..90fb2b66c 100644
--- a/ui/app/components/send/currency-display.js
+++ b/ui/app/components/send/currency-display.js
@@ -89,7 +89,6 @@ CurrencyDisplay.prototype.render = function () {
} = this.props
const valueToRender = this.getValueToRender()
-
const convertedValueToRender = this.getConvertedValueToRender(valueToRender)
return h('div', {
@@ -97,22 +96,24 @@ CurrencyDisplay.prototype.render = function () {
style: {
borderColor: inError ? 'red' : null,
},
- onClick: () => this.currencyInput.focus(),
+ onClick: () => this.currencyInput && this.currencyInput.focus(),
}, [
h('div.currency-display__primary-row', [
h('div.currency-display__input-wrapper', [
- h(CurrencyInput, {
+ h(readOnly ? 'input' : CurrencyInput, {
className: primaryBalanceClassName,
value: `${valueToRender}`,
placeholder: '0',
readOnly,
- onInputChange: newValue => {
- handleChange(this.getAmount(newValue))
- },
- inputRef: input => { this.currencyInput = input },
+ ...(!readOnly ? {
+ onInputChange: newValue => {
+ handleChange(this.getAmount(newValue))
+ },
+ inputRef: input => { this.currencyInput = input },
+ } : {}),
}),
h('span.currency-display__currency-symbol', primaryCurrency),
diff --git a/ui/app/components/send/send-constants.js b/ui/app/components/send/send-constants.js
index b3ee0899a..5d89c74aa 100644
--- a/ui/app/components/send/send-constants.js
+++ b/ui/app/components/send/send-constants.js
@@ -1,8 +1,8 @@
const ethUtil = require('ethereumjs-util')
const { conversionUtil, multiplyCurrencies } = require('../../conversion-util')
-const MIN_GAS_PRICE_HEX = (100000000).toString(16)
-const MIN_GAS_PRICE_DEC = '100000000'
+const MIN_GAS_PRICE_DEC = '0'
+const MIN_GAS_PRICE_HEX = (parseInt(MIN_GAS_PRICE_DEC)).toString(16)
const MIN_GAS_LIMIT_DEC = '21000'
const MIN_GAS_LIMIT_HEX = (parseInt(MIN_GAS_LIMIT_DEC)).toString(16)
diff --git a/ui/app/components/shapeshift-form.js b/ui/app/components/shapeshift-form.js
index fd4a80a4a..22ab64426 100644
--- a/ui/app/components/shapeshift-form.js
+++ b/ui/app/components/shapeshift-form.js
@@ -55,6 +55,10 @@ function ShapeshiftForm () {
}
}
+ShapeshiftForm.prototype.getCoinPair = function () {
+ return `${this.state.depositCoin.toUpperCase()}_ETH`
+}
+
ShapeshiftForm.prototype.componentWillMount = function () {
this.props.shapeShiftSubview()
}
@@ -120,14 +124,12 @@ ShapeshiftForm.prototype.renderMetadata = function (label, value) {
}
ShapeshiftForm.prototype.renderMarketInfo = function () {
- const { depositCoin } = this.state
- const coinPair = `${depositCoin}_eth`
const { tokenExchangeRates } = this.props
const {
limit,
rate,
minimum,
- } = tokenExchangeRates[coinPair] || {}
+ } = tokenExchangeRates[this.getCoinPair()] || {}
return h('div.shapeshift-form__metadata', {}, [
@@ -172,10 +174,9 @@ ShapeshiftForm.prototype.renderQrCode = function () {
ShapeshiftForm.prototype.render = function () {
const { coinOptions, btnClass, warning } = this.props
- const { depositCoin, errorMessage, showQrCode, depositAddress } = this.state
- const coinPair = `${depositCoin}_eth`
+ const { errorMessage, showQrCode, depositAddress } = this.state
const { tokenExchangeRates } = this.props
- const token = tokenExchangeRates[coinPair]
+ const token = tokenExchangeRates[this.getCoinPair()]
return h('div.shapeshift-form-wrapper', [
showQrCode
diff --git a/ui/app/components/spinner/index.js b/ui/app/components/spinner/index.js
new file mode 100644
index 000000000..9589efcf0
--- /dev/null
+++ b/ui/app/components/spinner/index.js
@@ -0,0 +1,2 @@
+const Spinner = require('./spinner.component')
+module.exports = Spinner
diff --git a/ui/app/components/spinner/spinner.component.js b/ui/app/components/spinner/spinner.component.js
new file mode 100644
index 000000000..b9a2eb52a
--- /dev/null
+++ b/ui/app/components/spinner/spinner.component.js
@@ -0,0 +1,78 @@
+import React from 'react'
+import PropTypes from 'prop-types'
+
+const Spinner = ({ className = '', color = '#000000' }) => {
+ return (
+ <div className={`spinner ${className}`}>
+ <svg className="lds-spinner" width="100%" height="100%" xmlns="http://www.w3.org/2000/svg" xmlnsXlink="http://www.w3.org/1999/xlink" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid" style={{background: 'none'}}>
+ <g transform="rotate(0 50 50)">
+ <rect x={45} y={0} rx={0} ry={0} width={10} height={30} fill={color}>
+ <animate attributeName="opacity" values="1;0" dur="1s" begin="-0.9166666666666666s" repeatCount="indefinite" />
+ </rect>
+ </g>
+ <g transform="rotate(30 50 50)">
+ <rect x={45} y={0} rx={0} ry={0} width={10} height={30} fill={color}>
+ <animate attributeName="opacity" values="1;0" dur="1s" begin="-0.8333333333333334s" repeatCount="indefinite" />
+ </rect>
+ </g>
+ <g transform="rotate(60 50 50)">
+ <rect x={45} y={0} rx={0} ry={0} width={10} height={30} fill={color}>
+ <animate attributeName="opacity" values="1;0" dur="1s" begin="-0.75s" repeatCount="indefinite" />
+ </rect>
+ </g>
+ <g transform="rotate(90 50 50)">
+ <rect x={45} y={0} rx={0} ry={0} width={10} height={30} fill={color}>
+ <animate attributeName="opacity" values="1;0" dur="1s" begin="-0.6666666666666666s" repeatCount="indefinite" />
+ </rect>
+ </g>
+ <g transform="rotate(120 50 50)">
+ <rect x={45} y={0} rx={0} ry={0} width={10} height={30} fill={color}>
+ <animate attributeName="opacity" values="1;0" dur="1s" begin="-0.5833333333333334s" repeatCount="indefinite" />
+ </rect>
+ </g>
+ <g transform="rotate(150 50 50)">
+ <rect x={45} y={0} rx={0} ry={0} width={10} height={30} fill={color}>
+ <animate attributeName="opacity" values="1;0" dur="1s" begin="-0.5s" repeatCount="indefinite" />
+ </rect>
+ </g>
+ <g transform="rotate(180 50 50)">
+ <rect x={45} y={0} rx={0} ry={0} width={10} height={30} fill={color}>
+ <animate attributeName="opacity" values="1;0" dur="1s" begin="-0.4166666666666667s" repeatCount="indefinite" />
+ </rect>
+ </g>
+ <g transform="rotate(210 50 50)">
+ <rect x={45} y={0} rx={0} ry={0} width={10} height={30} fill={color}>
+ <animate attributeName="opacity" values="1;0" dur="1s" begin="-0.3333333333333333s" repeatCount="indefinite" />
+ </rect>
+ </g>
+ <g transform="rotate(240 50 50)">
+ <rect x={45} y={0} rx={0} ry={0} width={10} height={30} fill={color}>
+ <animate attributeName="opacity" values="1;0" dur="1s" begin="-0.25s" repeatCount="indefinite" />
+ </rect>
+ </g>
+ <g transform="rotate(270 50 50)">
+ <rect x={45} y={0} rx={0} ry={0} width={10} height={30} fill={color}>
+ <animate attributeName="opacity" values="1;0" dur="1s" begin="-0.16666666666666666s" repeatCount="indefinite" />
+ </rect>
+ </g>
+ <g transform="rotate(300 50 50)">
+ <rect x={45} y={0} rx={0} ry={0} width={10} height={30} fill={color}>
+ <animate attributeName="opacity" values="1;0" dur="1s" begin="-0.08333333333333333s" repeatCount="indefinite" />
+ </rect>
+ </g>
+ <g transform="rotate(330 50 50)">
+ <rect x={45} y={0} rx={0} ry={0} width={10} height={30} fill={color}>
+ <animate attributeName="opacity" values="1;0" dur="1s" begin="0s" repeatCount="indefinite" />
+ </rect>
+ </g>
+ </svg>
+ </div>
+ )
+}
+
+Spinner.propTypes = {
+ className: PropTypes.string,
+ color: PropTypes.string,
+}
+
+module.exports = Spinner
diff --git a/ui/app/conf-tx.js b/ui/app/conf-tx.js
index b71538e31..fb38aaa76 100644
--- a/ui/app/conf-tx.js
+++ b/ui/app/conf-tx.js
@@ -13,7 +13,7 @@ const SignatureRequest = require('./components/signature-request')
// const PendingMsg = require('./components/pending-msg')
// const PendingPersonalMsg = require('./components/pending-personal-msg')
// const PendingTypedMsg = require('./components/pending-typed-msg')
-const Loading = require('./components/loading')
+const Loading = require('./components/loading-screen')
const { DEFAULT_ROUTE } = require('./routes')
module.exports = compose(
diff --git a/ui/app/css/itcss/components/index.scss b/ui/app/css/itcss/components/index.scss
index 959eb9d15..1c544e162 100644
--- a/ui/app/css/itcss/components/index.scss
+++ b/ui/app/css/itcss/components/index.scss
@@ -61,3 +61,5 @@
@import './welcome-screen.scss';
@import './sender-to-recipient.scss';
+
+@import '../../../components/export-text-container/export-text-container.scss';
diff --git a/ui/app/css/itcss/components/loading-overlay.scss b/ui/app/css/itcss/components/loading-overlay.scss
index a92fffec5..c18b7fa59 100644
--- a/ui/app/css/itcss/components/loading-overlay.scss
+++ b/ui/app/css/itcss/components/loading-overlay.scss
@@ -26,4 +26,25 @@
width: 100vw;
margin-top: 0;
}
+
+ &__container {
+ position: absolute;
+ top: 33%;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ }
+
+ &__message {
+ margin-top: 32px;
+ font-weight: 400;
+ font-size: 20px;
+ color: $manatee;
+ }
+}
+
+.spinner {
+ height: 58px;
+ width: 58px;
}
diff --git a/ui/app/css/itcss/components/pages/index.scss b/ui/app/css/itcss/components/pages/index.scss
index 82446fd7a..d0b59da53 100644
--- a/ui/app/css/itcss/components/pages/index.scss
+++ b/ui/app/css/itcss/components/pages/index.scss
@@ -1 +1,3 @@
@import './unlock.scss';
+
+@import './reveal-seed.scss';
diff --git a/ui/app/css/itcss/components/pages/reveal-seed.scss b/ui/app/css/itcss/components/pages/reveal-seed.scss
new file mode 100644
index 000000000..b8f13af4a
--- /dev/null
+++ b/ui/app/css/itcss/components/pages/reveal-seed.scss
@@ -0,0 +1,17 @@
+.reveal-seed {
+ &__content {
+ padding: 20px;
+ }
+
+ &__label {
+ padding-bottom: 10px;
+ font-weight: 400;
+ display: inline-block;
+ }
+
+ &__error {
+ color: $crimson;
+ font-size: 14px;
+ padding-top: 5px;
+ }
+}
diff --git a/ui/app/css/itcss/generic/index.scss b/ui/app/css/itcss/generic/index.scss
index 92321394b..7a64810c4 100644
--- a/ui/app/css/itcss/generic/index.scss
+++ b/ui/app/css/itcss/generic/index.scss
@@ -207,6 +207,27 @@ input.large-input {
&__content {
height: 100%;
overflow-y: auto;
+ min-height: 250px;
+ max-height: 400px;
+ }
+
+ &__warning-container {
+ background: $linen;
+ padding: 20px;
+ display: flex;
+ align-items: start;
+ }
+
+ &__warning-message {
+ padding-left: 15px;
+ }
+
+ &__warning-title {
+ font-weight: 500;
+ }
+
+ &__warning-icon {
+ padding-top: 5px;
}
}
@@ -237,3 +258,49 @@ input.large-input {
border-radius: 0;
}
}
+
+@media screen and (min-width: 576px) {
+ .page-container {
+ height: 600px;
+ flex: 0 0 auto;
+ }
+}
+
+.input-label {
+ padding-bottom: 10px;
+ font-weight: 400;
+ display: inline-block;
+}
+
+input.form-control {
+ padding-left: 10px;
+ font-size: 14px;
+ height: 40px;
+ border: 1px solid $alto;
+ border-radius: 3px;
+ width: 100%;
+
+ &::-webkit-input-placeholder {
+ font-weight: 100;
+ color: $dusty-gray;
+ }
+
+ &::-moz-placeholder {
+ font-weight: 100;
+ color: $dusty-gray;
+ }
+
+ &:-ms-input-placeholder {
+ font-weight: 100;
+ color: $dusty-gray;
+ }
+
+ &:-moz-placeholder {
+ font-weight: 100;
+ color: $dusty-gray;
+ }
+
+ &--error {
+ border: 1px solid $monzo;
+ }
+}
diff --git a/ui/app/css/itcss/settings/variables.scss b/ui/app/css/itcss/settings/variables.scss
index 51548306f..814d7a382 100644
--- a/ui/app/css/itcss/settings/variables.scss
+++ b/ui/app/css/itcss/settings/variables.scss
@@ -54,6 +54,7 @@ $saffron: #f6c343;
$dodger-blue: #3099f2;
$zumthor: #edf7ff;
$ecstasy: #f7861c;
+$linen: #fdf4f4;
/*
Z-Indicies
diff --git a/ui/app/keychains/hd/recover-seed/confirmation.js b/ui/app/keychains/hd/recover-seed/confirmation.js
deleted file mode 100644
index eb588415f..000000000
--- a/ui/app/keychains/hd/recover-seed/confirmation.js
+++ /dev/null
@@ -1,138 +0,0 @@
-const inherits = require('util').inherits
-const Component = require('react').Component
-const PropTypes = require('prop-types')
-const connect = require('react-redux').connect
-const h = require('react-hyperscript')
-const actions = require('../../../actions')
-const { withRouter } = require('react-router-dom')
-const { compose } = require('recompose')
-const {
- DEFAULT_ROUTE,
- INITIALIZE_BACKUP_PHRASE_ROUTE,
-} = require('../../../routes')
-
-RevealSeedConfirmation.contextTypes = {
- t: PropTypes.func,
-}
-
-module.exports = compose(
- withRouter,
- connect(mapStateToProps)
-)(RevealSeedConfirmation)
-
-
-inherits(RevealSeedConfirmation, Component)
-function RevealSeedConfirmation () {
- Component.call(this)
-}
-
-function mapStateToProps (state) {
- return {
- warning: state.appState.warning,
- }
-}
-
-RevealSeedConfirmation.prototype.render = function () {
- const props = this.props
-
- return (
-
- h('.initialize-screen.flex-column.flex-center.flex-grow', {
- style: { maxWidth: '420px' },
- }, [
-
- h('h3.flex-center.text-transform-uppercase', {
- style: {
- background: '#EBEBEB',
- color: '#AEAEAE',
- marginBottom: 24,
- width: '100%',
- fontSize: '20px',
- padding: 6,
- },
- }, [
- 'Reveal Seed Words',
- ]),
-
- h('.div', {
- style: {
- display: 'flex',
- flexDirection: 'column',
- padding: '20px',
- justifyContent: 'center',
- },
- }, [
-
- h('h4', this.context.t('revealSeedWordsWarning')),
-
- // confirmation
- h('input.large-input.letter-spacey', {
- type: 'password',
- id: 'password-box',
- placeholder: this.context.t('enterPasswordConfirm'),
- onKeyPress: this.checkConfirmation.bind(this),
- style: {
- width: 260,
- marginTop: '12px',
- },
- }),
-
- h('.flex-row.flex-start', {
- style: {
- marginTop: 30,
- width: '50%',
- },
- }, [
- // cancel
- h('button.primary', {
- onClick: this.goHome.bind(this),
- }, 'CANCEL'),
-
- // submit
- h('button.primary', {
- style: { marginLeft: '10px' },
- onClick: this.revealSeedWords.bind(this),
- }, 'OK'),
-
- ]),
-
- (props.warning) && (
- h('span.error', {
- style: {
- margin: '20px',
- },
- }, props.warning.split('-'))
- ),
-
- props.inProgress && (
- h('span.in-progress-notification', this.context.t('generatingSeed'))
- ),
- ]),
- ])
- )
-}
-
-RevealSeedConfirmation.prototype.componentDidMount = function () {
- document.getElementById('password-box').focus()
-}
-
-RevealSeedConfirmation.prototype.goHome = function () {
- this.props.dispatch(actions.showConfigPage(false))
- this.props.dispatch(actions.confirmSeedWords())
- .then(() => this.props.history.push(DEFAULT_ROUTE))
-}
-
-// create vault
-
-RevealSeedConfirmation.prototype.checkConfirmation = function (event) {
- if (event.key === 'Enter') {
- event.preventDefault()
- this.revealSeedWords()
- }
-}
-
-RevealSeedConfirmation.prototype.revealSeedWords = function () {
- var password = document.getElementById('password-box').value
- this.props.dispatch(actions.requestRevealSeed(password))
- .then(() => this.props.history.push(INITIALIZE_BACKUP_PHRASE_ROUTE))
-}
diff --git a/ui/app/send-v2.js b/ui/app/send-v2.js
index 30d3d3152..bd00b186e 100644
--- a/ui/app/send-v2.js
+++ b/ui/app/send-v2.js
@@ -493,7 +493,7 @@ SendTransactionScreen.prototype.renderFooter = function () {
history,
} = this.props
- const missingTokenBalance = selectedToken && !tokenBalance
+ const missingTokenBalance = selectedToken && (tokenBalance === null || tokenBalance === undefined)
const noErrors = !amountError && toError === null
return h('div.page-container__footer', [
diff --git a/ui/app/token-util.js b/ui/app/token-util.js
index f84051ef5..920442bfc 100644
--- a/ui/app/token-util.js
+++ b/ui/app/token-util.js
@@ -1,14 +1,6 @@
-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 util = require('./util')
+function tokenInfoGetter () {
const tokens = {}
return async (address) => {
@@ -16,18 +8,35 @@ const tokenInfoGetter = function () {
return tokens[address]
}
- const contract = TokenContract.at(address)
+ tokens[address] = await getSymbolAndDecimals(address)
- const result = await Promise.all([
- contract.symbol(),
- contract.decimals(),
- ])
+ return tokens[address]
+ }
+}
- const [ symbol = [], decimals = [] ] = result
+async function getSymbolAndDecimals (tokenAddress, existingTokens = []) {
+ const existingToken = existingTokens.find(({ address }) => tokenAddress === address)
+ if (existingToken) {
+ return existingToken
+ }
+
+ let result = []
+ try {
+ const token = util.getContractAtAddress(tokenAddress)
+
+ result = await Promise.all([
+ token.symbol(),
+ token.decimals(),
+ ])
+ } catch (err) {
+ console.log(`symbol() and decimal() calls for token at address ${tokenAddress} resulted in error:`, err)
+ }
- tokens[address] = { symbol: symbol[0], decimals: decimals[0] }
+ const [ symbol = [], decimals = [] ] = result
- return tokens[address]
+ return {
+ symbol: symbol[0] || null,
+ decimals: decimals[0] && decimals[0].toString() || null,
}
}
@@ -42,4 +51,5 @@ function calcTokenAmount (value, decimals) {
module.exports = {
tokenInfoGetter,
calcTokenAmount,
+ getSymbolAndDecimals,
}