diff --git a/common/components/AmountFieldFactory/AmountInputFactory.tsx b/common/components/AmountFieldFactory/AmountInputFactory.tsx index a2ddfcfe..1c52c476 100644 --- a/common/components/AmountFieldFactory/AmountInputFactory.tsx +++ b/common/components/AmountFieldFactory/AmountInputFactory.tsx @@ -1,6 +1,6 @@ import React, { Component } from 'react'; import { Query } from 'components/renderCbs'; -import { ICurrentValue, getCurrentValue, isValidAmount } from 'selectors/transaction'; +import { ICurrentValue, getCurrentValue, nonStandardTransaction } from 'selectors/transaction'; import { AppState } from 'reducers'; import { connect } from 'react-redux'; import { CallbackProps } from 'components/AmountFieldFactory'; @@ -11,15 +11,15 @@ interface OwnProps { } interface StateProps { + isNonStandard: boolean; currentValue: ICurrentValue; - validAmount: boolean; } type Props = OwnProps & StateProps; class AmountInputClass extends Component { public render() { - const { currentValue, onChange, withProps, validAmount } = this.props; + const { currentValue, onChange, withProps, isNonStandard } = this.props; return ( { withQuery={({ readOnly }) => withProps({ currentValue, - isValid: validAmount, + isValid: !!currentValue.value || isNonStandard, readOnly: !!readOnly, onChange }) @@ -39,5 +39,5 @@ class AmountInputClass extends Component { export const AmountInput = connect((state: AppState) => ({ currentValue: getCurrentValue(state), - validAmount: isValidAmount(state) + isNonStandard: nonStandardTransaction(state) }))(AmountInputClass); diff --git a/common/components/GenerateTransaction/GenerateTransaction.tsx b/common/components/GenerateTransaction/GenerateTransaction.tsx index b84ef4ea..724a9e2f 100644 --- a/common/components/GenerateTransaction/GenerateTransaction.tsx +++ b/common/components/GenerateTransaction/GenerateTransaction.tsx @@ -7,7 +7,6 @@ import { AppState } from 'reducers'; import { getTransaction, isNetworkRequestPending, - isValidAmount, isValidGasPrice, isValidGasLimit } from 'selectors/transaction'; @@ -18,7 +17,6 @@ interface StateProps { networkRequestPending: boolean; isFullTransaction: boolean; isWeb3Wallet: boolean; - validAmount: boolean; validGasPrice: boolean; validGasLimit: boolean; } @@ -30,17 +28,13 @@ class GenerateTransactionClass extends Component { isWeb3Wallet, transaction, networkRequestPending, - validAmount, + validGasPrice, validGasLimit } = this.props; const isButtonDisabled = - !isFullTransaction || - networkRequestPending || - !validAmount || - !validGasPrice || - !validGasLimit; + !isFullTransaction || networkRequestPending || !validGasPrice || !validGasLimit; return ( ({ ...getTransaction(state), networkRequestPending: isNetworkRequestPending(state), isWeb3Wallet: getWalletType(state).isWeb3Wallet, - validAmount: isValidAmount(state), validGasPrice: isValidGasPrice(state), validGasLimit: isValidGasLimit(state) }))(GenerateTransactionClass); diff --git a/common/sagas/swap/liteSend.ts b/common/sagas/swap/liteSend.ts index 72c05ec3..8779480d 100644 --- a/common/sagas/swap/liteSend.ts +++ b/common/sagas/swap/liteSend.ts @@ -15,7 +15,7 @@ import { isSupportedUnit } from 'selectors/config'; import { isEtherUnit } from 'libs/units'; import { showLiteSend, configureLiteSend } from 'actions/swap'; import { TypeKeys as SwapTK } from 'actions/swap/constants'; -import { isUnlocked } from 'selectors/wallet'; +import { isUnlocked, isEtherBalancePending } from 'selectors/wallet'; type SwapState = AppState['swap']; @@ -48,6 +48,11 @@ export function* configureLiteSendSaga(): SagaIterator { WalletTK.WALLET_SET_TOKEN_BALANCE_FULFILLED, WalletTK.WALLET_SET_TOKEN_BALANCE_REJECTED ]); + } else { + const etherBalanceResolving: boolean = yield select(isEtherBalancePending); + if (etherBalanceResolving) { + yield take([WalletTK.WALLET_SET_BALANCE_FULFILLED, WalletTK.WALLET_SET_BALANCE_REJECTED]); + } } yield put(setUnitMeta(id)); diff --git a/common/selectors/transaction/current.ts b/common/selectors/transaction/current.ts index 780f2184..3d7ea625 100644 --- a/common/selectors/transaction/current.ts +++ b/common/selectors/transaction/current.ts @@ -3,9 +3,7 @@ import { getUnit, getTokenTo, getTokenValue } from './meta'; import { AppState } from 'reducers'; import { isEtherUnit, TokenValue, Wei, Address } from 'libs/units'; import { gasPriceValidator, gasLimitValidator } from 'libs/validators'; -import { getDataExists, getValidGasCost, getGasPrice, getGasLimit } from 'selectors/transaction'; -import { getCurrentBalance } from 'selectors/wallet'; -import { getOffline } from 'selectors/config'; +import { getDataExists, getGasPrice, getGasLimit } from 'selectors/transaction'; interface ICurrentValue { raw: string; @@ -40,55 +38,6 @@ const isValidCurrentTo = (state: AppState) => { } }; -const isValidAmount = (state: AppState): boolean => { - const currentValue = getCurrentValue(state); - const dataExists = getDataExists(state); - const validGasCost = getValidGasCost(state); - const isOffline = getOffline(state); - - // If value is an empty string, mark as invalid - if (!currentValue.raw) { - return false; - } - - // If offline, assume amount is valid - if (isOffline) { - return true; - } - - // We do some wallet validation here. - // For some reason with MetaMask, sometimes the currentValue.value is not a null - // but instead a BN with a value equal to currentValue.raw - even though the wallet - // doesn't have enough of a balance. - - // Get the wallet balance (token value or ether value) - const walletBalance = getCurrentBalance(state); - - // We ensure that we have a valid walletBalance (token or Ether is fine) - if (!walletBalance) { - return false; - } - - if (isEtherTransaction(state)) { - // if data exists with no value, just check if gas is enough - if (dataExists && !currentValue.value && currentValue.raw === '') { - return validGasCost; - } - // if the currentValue.value is not null, then compare it against the walletBalance. - if (currentValue.value) { - return walletBalance.cmp(currentValue.value) > 0; - } - - return !!currentValue.value; - } else { - // if the currentValue.value is not null, then compare it against the walletBalance. - if (currentValue.value) { - return walletBalance.cmp(currentValue.value) > 0; - } - return !!currentValue.value; - } -}; - const isValidGasPrice = (state: AppState): boolean => gasPriceValidator(getGasPrice(state).raw); const isValidGasLimit = (state: AppState): boolean => gasLimitValidator(getGasLimit(state).raw); @@ -100,7 +49,6 @@ export { ICurrentTo, isEtherTransaction, isValidCurrentTo, - isValidAmount, isValidGasPrice, isValidGasLimit }; diff --git a/common/selectors/wallet.ts b/common/selectors/wallet.ts index ca0e4763..c3347b1d 100644 --- a/common/selectors/wallet.ts +++ b/common/selectors/wallet.ts @@ -108,6 +108,9 @@ export const getWalletType = (state: AppState): IWalletType => { export const isUnlocked = (state: AppState) => !!getWalletInst(state); +export const isEtherBalancePending = (state: AppState): boolean => + getWallet(state).balance.isPending; + export const getEtherBalance = (state: AppState): Wei | null => getWallet(state).balance.wei; export const getCurrentBalance = (state: AppState): Wei | TokenValue | null => { diff --git a/webpack_config/makeConfig.js b/webpack_config/makeConfig.js index 843d75ab..502c6fd5 100644 --- a/webpack_config/makeConfig.js +++ b/webpack_config/makeConfig.js @@ -48,62 +48,71 @@ module.exports = function(opts = {}) { // Typescript if (options.isProduction || !process.env.SLOW_BUILD_SPEED) { rules.push(config.typescriptRule); - } - else { - threadLoader.warmup( - config.typescriptRule.use[0].options, - [config.typescriptRule.use[0].loader] - ); + } else { + threadLoader.warmup(config.typescriptRule.use[0].options, [ + config.typescriptRule.use[0].loader + ]); rules.push({ ...config.typescriptRule, - use: [{ - loader: 'thread-loader', - options: { - workers: 4 - } - }, ...config.typescriptRule.use], + use: [ + { + loader: 'thread-loader', + options: { + workers: 4 + } + }, + ...config.typescriptRule.use + ] }); } // Styles (CSS, SCSS, LESS) if (options.isProduction) { - rules.push({ - test: /\.css$/, - use: ExtractTextPlugin.extract({ - fallback: 'style-loader', - use: 'css-loader' - }) - }, { - test: /\.scss$/, - use: ExtractTextPlugin.extract({ - fallback: 'style-loader', - use: ['css-loader', 'sass-loader'] - }) - }, { - test: /\.less$/, - use: ExtractTextPlugin.extract({ - fallback: 'style-loader', - use: ['css-loader', 'less-loader'] - }) - }); + rules.push( + { + test: /\.css$/, + use: ExtractTextPlugin.extract({ + fallback: 'style-loader', + use: 'css-loader' + }) + }, + { + test: /\.scss$/, + use: ExtractTextPlugin.extract({ + fallback: 'style-loader', + use: ['css-loader', 'sass-loader'] + }) + }, + { + test: /\.less$/, + use: ExtractTextPlugin.extract({ + fallback: 'style-loader', + use: ['css-loader', 'less-loader'] + }) + } + ); } else { - rules.push({ - test: /\.css$/, - include: path.resolve(config.path.src, 'vendor'), - use: ['style-loader', 'css-loader'] - }, { - test: /\.scss$/, - include: ['components', 'containers', 'sass'] - .map(dir => path.resolve(config.path.src, dir)) - .concat([config.path.modules]), + rules.push( + { + test: /\.css$/, + include: path.resolve(config.path.src, 'vendor'), + use: ['style-loader', 'css-loader'] + }, + { + test: /\.scss$/, + include: ['components', 'containers', 'sass'] + .map(dir => path.resolve(config.path.src, dir)) + .concat([config.path.modules]), - exclude: /node_modules(?!\/font-awesome)/, - use: ['style-loader', 'css-loader', 'sass-loader'] - }, { - test: /\.less$/, - include: path.resolve(config.path.assets, 'styles'), - use: ['style-loader', 'css-loader', 'less-loader'] - }); + exclude: /node_modules(?!\/font-awesome)/, + use: ['style-loader', 'css-loader', 'sass-loader'] + }, + { + test: /\.less$/, + include: path.resolve(config.path.assets, 'styles'), + use: ['style-loader', 'css-loader', 'less-loader'] + } + ); } // Web workers @@ -114,10 +123,7 @@ module.exports = function(opts = {}) { // Images rules.push({ - include: [ - path.resolve(config.path.assets), - path.resolve(config.path.modules) - ], + include: [path.resolve(config.path.assets), path.resolve(config.path.modules)], exclude: /node_modules(?!\/font-awesome)/, test: /\.(gif|png|jpe?g|svg)$/i, use: [ @@ -152,10 +158,7 @@ module.exports = function(opts = {}) { // Fonts rules.push({ - include: [ - path.resolve(config.path.assets), - path.resolve(config.path.modules) - ], + include: [path.resolve(config.path.assets), path.resolve(config.path.modules)], exclude: /node_modules(?!\/font-awesome)/, test: /\.(ico|eot|otf|webp|ttf|woff|woff2)(\?.*)?$/, loader: 'file-loader' @@ -193,21 +196,24 @@ module.exports = function(opts = {}) { 'process.env.BUILD_DOWNLOADABLE': JSON.stringify(isDownloadable), 'process.env.BUILD_HTML': JSON.stringify(options.isHTMLBuild), 'process.env.BUILD_ELECTRON': JSON.stringify(options.isElectronBuild) - }), + }) ]; if (options.isProduction) { plugins.push( - new BabelMinifyPlugin({ - // Mangle seems to be reusing variable identifiers, causing errors - mangle: false, - // These two on top of a lodash file are causing illegal characters for - // safari and ios browsers - evaluate: false, - propertyLiterals: false, - }, { - comments: false - }), + new BabelMinifyPlugin( + { + // Mangle seems to be reusing variable identifiers, causing errors + mangle: false, + // These two on top of a lodash file are causing illegal characters for + // safari and ios browsers + evaluate: false, + propertyLiterals: false + }, + { + comments: false + } + ), new webpack.optimize.CommonsChunkPlugin({ name: 'vendor', filename: 'vendor.[chunkhash:8].js' @@ -225,9 +231,8 @@ module.exports = function(opts = {}) { new ProgressPlugin(), new ClearDistPlugin(), new SortCachePlugin() - ) - } - else { + ); + } else { plugins.push( new AutoDllPlugin({ inject: true, // will inject the DLL bundles to index.html @@ -235,12 +240,7 @@ module.exports = function(opts = {}) { debug: true, context: path.join(config.path.root), entry: { - vendor: [ - ...config.vendorModules, - 'babel-polyfill', - 'bootstrap-sass', - 'font-awesome' - ] + vendor: [...config.vendorModules, 'babel-polyfill', 'bootstrap-sass', 'font-awesome'] } }), new HardSourceWebpackPlugin({ @@ -259,19 +259,21 @@ module.exports = function(opts = {}) { if (options.isElectronBuild) { // target: 'electron-renderer' kills scrypt, so manually pull in some // of its configuration instead - plugins.push(new webpack.ExternalsPlugin("commonjs", [ - "desktop-capturer", - "electron", - "ipc", - "ipc-renderer", - "remote", - "web-frame", - "clipboard", - "crash-reporter", - "native-image", - "screen", - "shell" - ])); + plugins.push( + new webpack.ExternalsPlugin('commonjs', [ + 'desktop-capturer', + 'electron', + 'ipc', + 'ipc-renderer', + 'remote', + 'web-frame', + 'clipboard', + 'crash-reporter', + 'native-image', + 'screen', + 'shell' + ]) + ); } // ==================== @@ -281,8 +283,7 @@ module.exports = function(opts = {}) { if (!options.isProduction) { if (process.env.SLOW_BUILD_SPEED) { devtool = 'source-map'; - } - else { + } else { devtool = 'cheap-module-eval-source-map'; } } @@ -295,11 +296,11 @@ module.exports = function(opts = {}) { filename: options.isProduction ? '[name].[chunkhash:8].js' : '[name].js', publicPath: isDownloadable && options.isProduction ? './' : '/', crossOriginLoading: 'anonymous' - } - + }; // The final bundle return { + devtool, entry, output, module: { rules }, @@ -318,4 +319,4 @@ module.exports = function(opts = {}) { modules: false } }; -} +};