Fix misc send issues (#916)

This commit is contained in:
HenryNguyen5 2018-01-24 21:41:39 -05:00 committed by Daniel Ternyak
parent 2fd4730ebe
commit 22c107fe4c
6 changed files with 111 additions and 161 deletions

View File

@ -1,6 +1,6 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import { Query } from 'components/renderCbs'; import { Query } from 'components/renderCbs';
import { ICurrentValue, getCurrentValue, isValidAmount } from 'selectors/transaction'; import { ICurrentValue, getCurrentValue, nonStandardTransaction } from 'selectors/transaction';
import { AppState } from 'reducers'; import { AppState } from 'reducers';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { CallbackProps } from 'components/AmountFieldFactory'; import { CallbackProps } from 'components/AmountFieldFactory';
@ -11,15 +11,15 @@ interface OwnProps {
} }
interface StateProps { interface StateProps {
isNonStandard: boolean;
currentValue: ICurrentValue; currentValue: ICurrentValue;
validAmount: boolean;
} }
type Props = OwnProps & StateProps; type Props = OwnProps & StateProps;
class AmountInputClass extends Component<Props> { class AmountInputClass extends Component<Props> {
public render() { public render() {
const { currentValue, onChange, withProps, validAmount } = this.props; const { currentValue, onChange, withProps, isNonStandard } = this.props;
return ( return (
<Query <Query
@ -27,7 +27,7 @@ class AmountInputClass extends Component<Props> {
withQuery={({ readOnly }) => withQuery={({ readOnly }) =>
withProps({ withProps({
currentValue, currentValue,
isValid: validAmount, isValid: !!currentValue.value || isNonStandard,
readOnly: !!readOnly, readOnly: !!readOnly,
onChange onChange
}) })
@ -39,5 +39,5 @@ class AmountInputClass extends Component<Props> {
export const AmountInput = connect((state: AppState) => ({ export const AmountInput = connect((state: AppState) => ({
currentValue: getCurrentValue(state), currentValue: getCurrentValue(state),
validAmount: isValidAmount(state) isNonStandard: nonStandardTransaction(state)
}))(AmountInputClass); }))(AmountInputClass);

View File

@ -7,7 +7,6 @@ import { AppState } from 'reducers';
import { import {
getTransaction, getTransaction,
isNetworkRequestPending, isNetworkRequestPending,
isValidAmount,
isValidGasPrice, isValidGasPrice,
isValidGasLimit isValidGasLimit
} from 'selectors/transaction'; } from 'selectors/transaction';
@ -18,7 +17,6 @@ interface StateProps {
networkRequestPending: boolean; networkRequestPending: boolean;
isFullTransaction: boolean; isFullTransaction: boolean;
isWeb3Wallet: boolean; isWeb3Wallet: boolean;
validAmount: boolean;
validGasPrice: boolean; validGasPrice: boolean;
validGasLimit: boolean; validGasLimit: boolean;
} }
@ -30,17 +28,13 @@ class GenerateTransactionClass extends Component<StateProps> {
isWeb3Wallet, isWeb3Wallet,
transaction, transaction,
networkRequestPending, networkRequestPending,
validAmount,
validGasPrice, validGasPrice,
validGasLimit validGasLimit
} = this.props; } = this.props;
const isButtonDisabled = const isButtonDisabled =
!isFullTransaction || !isFullTransaction || networkRequestPending || !validGasPrice || !validGasLimit;
networkRequestPending ||
!validAmount ||
!validGasPrice ||
!validGasLimit;
return ( return (
<WithSigner <WithSigner
isWeb3={isWeb3Wallet} isWeb3={isWeb3Wallet}
@ -62,7 +56,6 @@ export const GenerateTransaction = connect((state: AppState) => ({
...getTransaction(state), ...getTransaction(state),
networkRequestPending: isNetworkRequestPending(state), networkRequestPending: isNetworkRequestPending(state),
isWeb3Wallet: getWalletType(state).isWeb3Wallet, isWeb3Wallet: getWalletType(state).isWeb3Wallet,
validAmount: isValidAmount(state),
validGasPrice: isValidGasPrice(state), validGasPrice: isValidGasPrice(state),
validGasLimit: isValidGasLimit(state) validGasLimit: isValidGasLimit(state)
}))(GenerateTransactionClass); }))(GenerateTransactionClass);

View File

@ -15,7 +15,7 @@ import { isSupportedUnit } from 'selectors/config';
import { isEtherUnit } from 'libs/units'; import { isEtherUnit } from 'libs/units';
import { showLiteSend, configureLiteSend } from 'actions/swap'; import { showLiteSend, configureLiteSend } from 'actions/swap';
import { TypeKeys as SwapTK } from 'actions/swap/constants'; import { TypeKeys as SwapTK } from 'actions/swap/constants';
import { isUnlocked } from 'selectors/wallet'; import { isUnlocked, isEtherBalancePending } from 'selectors/wallet';
type SwapState = AppState['swap']; type SwapState = AppState['swap'];
@ -48,6 +48,11 @@ export function* configureLiteSendSaga(): SagaIterator {
WalletTK.WALLET_SET_TOKEN_BALANCE_FULFILLED, WalletTK.WALLET_SET_TOKEN_BALANCE_FULFILLED,
WalletTK.WALLET_SET_TOKEN_BALANCE_REJECTED 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)); yield put(setUnitMeta(id));

View File

@ -3,9 +3,7 @@ import { getUnit, getTokenTo, getTokenValue } from './meta';
import { AppState } from 'reducers'; import { AppState } from 'reducers';
import { isEtherUnit, TokenValue, Wei, Address } from 'libs/units'; import { isEtherUnit, TokenValue, Wei, Address } from 'libs/units';
import { gasPriceValidator, gasLimitValidator } from 'libs/validators'; import { gasPriceValidator, gasLimitValidator } from 'libs/validators';
import { getDataExists, getValidGasCost, getGasPrice, getGasLimit } from 'selectors/transaction'; import { getDataExists, getGasPrice, getGasLimit } from 'selectors/transaction';
import { getCurrentBalance } from 'selectors/wallet';
import { getOffline } from 'selectors/config';
interface ICurrentValue { interface ICurrentValue {
raw: string; 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 isValidGasPrice = (state: AppState): boolean => gasPriceValidator(getGasPrice(state).raw);
const isValidGasLimit = (state: AppState): boolean => gasLimitValidator(getGasLimit(state).raw); const isValidGasLimit = (state: AppState): boolean => gasLimitValidator(getGasLimit(state).raw);
@ -100,7 +49,6 @@ export {
ICurrentTo, ICurrentTo,
isEtherTransaction, isEtherTransaction,
isValidCurrentTo, isValidCurrentTo,
isValidAmount,
isValidGasPrice, isValidGasPrice,
isValidGasLimit isValidGasLimit
}; };

View File

@ -108,6 +108,9 @@ export const getWalletType = (state: AppState): IWalletType => {
export const isUnlocked = (state: AppState) => !!getWalletInst(state); 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 getEtherBalance = (state: AppState): Wei | null => getWallet(state).balance.wei;
export const getCurrentBalance = (state: AppState): Wei | TokenValue | null => { export const getCurrentBalance = (state: AppState): Wei | TokenValue | null => {

View File

@ -48,62 +48,71 @@ module.exports = function(opts = {}) {
// Typescript // Typescript
if (options.isProduction || !process.env.SLOW_BUILD_SPEED) { if (options.isProduction || !process.env.SLOW_BUILD_SPEED) {
rules.push(config.typescriptRule); rules.push(config.typescriptRule);
} } else {
else { threadLoader.warmup(config.typescriptRule.use[0].options, [
threadLoader.warmup( config.typescriptRule.use[0].loader
config.typescriptRule.use[0].options, ]);
[config.typescriptRule.use[0].loader]
);
rules.push({ rules.push({
...config.typescriptRule, ...config.typescriptRule,
use: [{ use: [
loader: 'thread-loader', {
options: { loader: 'thread-loader',
workers: 4 options: {
} workers: 4
}, ...config.typescriptRule.use], }
},
...config.typescriptRule.use
]
}); });
} }
// Styles (CSS, SCSS, LESS) // Styles (CSS, SCSS, LESS)
if (options.isProduction) { if (options.isProduction) {
rules.push({ rules.push(
test: /\.css$/, {
use: ExtractTextPlugin.extract({ test: /\.css$/,
fallback: 'style-loader', use: ExtractTextPlugin.extract({
use: 'css-loader' fallback: 'style-loader',
}) use: 'css-loader'
}, { })
test: /\.scss$/, },
use: ExtractTextPlugin.extract({ {
fallback: 'style-loader', test: /\.scss$/,
use: ['css-loader', 'sass-loader'] use: ExtractTextPlugin.extract({
}) fallback: 'style-loader',
}, { use: ['css-loader', 'sass-loader']
test: /\.less$/, })
use: ExtractTextPlugin.extract({ },
fallback: 'style-loader', {
use: ['css-loader', 'less-loader'] test: /\.less$/,
}) use: ExtractTextPlugin.extract({
}); fallback: 'style-loader',
use: ['css-loader', 'less-loader']
})
}
);
} else { } else {
rules.push({ rules.push(
test: /\.css$/, {
include: path.resolve(config.path.src, 'vendor'), test: /\.css$/,
use: ['style-loader', 'css-loader'] 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)) test: /\.scss$/,
.concat([config.path.modules]), include: ['components', 'containers', 'sass']
.map(dir => path.resolve(config.path.src, dir))
.concat([config.path.modules]),
exclude: /node_modules(?!\/font-awesome)/, exclude: /node_modules(?!\/font-awesome)/,
use: ['style-loader', 'css-loader', 'sass-loader'] use: ['style-loader', 'css-loader', 'sass-loader']
}, { },
test: /\.less$/, {
include: path.resolve(config.path.assets, 'styles'), test: /\.less$/,
use: ['style-loader', 'css-loader', 'less-loader'] include: path.resolve(config.path.assets, 'styles'),
}); use: ['style-loader', 'css-loader', 'less-loader']
}
);
} }
// Web workers // Web workers
@ -114,10 +123,7 @@ module.exports = function(opts = {}) {
// Images // Images
rules.push({ rules.push({
include: [ include: [path.resolve(config.path.assets), path.resolve(config.path.modules)],
path.resolve(config.path.assets),
path.resolve(config.path.modules)
],
exclude: /node_modules(?!\/font-awesome)/, exclude: /node_modules(?!\/font-awesome)/,
test: /\.(gif|png|jpe?g|svg)$/i, test: /\.(gif|png|jpe?g|svg)$/i,
use: [ use: [
@ -152,10 +158,7 @@ module.exports = function(opts = {}) {
// Fonts // Fonts
rules.push({ rules.push({
include: [ include: [path.resolve(config.path.assets), path.resolve(config.path.modules)],
path.resolve(config.path.assets),
path.resolve(config.path.modules)
],
exclude: /node_modules(?!\/font-awesome)/, exclude: /node_modules(?!\/font-awesome)/,
test: /\.(ico|eot|otf|webp|ttf|woff|woff2)(\?.*)?$/, test: /\.(ico|eot|otf|webp|ttf|woff|woff2)(\?.*)?$/,
loader: 'file-loader' loader: 'file-loader'
@ -193,21 +196,24 @@ module.exports = function(opts = {}) {
'process.env.BUILD_DOWNLOADABLE': JSON.stringify(isDownloadable), 'process.env.BUILD_DOWNLOADABLE': JSON.stringify(isDownloadable),
'process.env.BUILD_HTML': JSON.stringify(options.isHTMLBuild), 'process.env.BUILD_HTML': JSON.stringify(options.isHTMLBuild),
'process.env.BUILD_ELECTRON': JSON.stringify(options.isElectronBuild) 'process.env.BUILD_ELECTRON': JSON.stringify(options.isElectronBuild)
}), })
]; ];
if (options.isProduction) { if (options.isProduction) {
plugins.push( plugins.push(
new BabelMinifyPlugin({ new BabelMinifyPlugin(
// Mangle seems to be reusing variable identifiers, causing errors {
mangle: false, // Mangle seems to be reusing variable identifiers, causing errors
// These two on top of a lodash file are causing illegal characters for mangle: false,
// safari and ios browsers // These two on top of a lodash file are causing illegal characters for
evaluate: false, // safari and ios browsers
propertyLiterals: false, evaluate: false,
}, { propertyLiterals: false
comments: false },
}), {
comments: false
}
),
new webpack.optimize.CommonsChunkPlugin({ new webpack.optimize.CommonsChunkPlugin({
name: 'vendor', name: 'vendor',
filename: 'vendor.[chunkhash:8].js' filename: 'vendor.[chunkhash:8].js'
@ -225,9 +231,8 @@ module.exports = function(opts = {}) {
new ProgressPlugin(), new ProgressPlugin(),
new ClearDistPlugin(), new ClearDistPlugin(),
new SortCachePlugin() new SortCachePlugin()
) );
} } else {
else {
plugins.push( plugins.push(
new AutoDllPlugin({ new AutoDllPlugin({
inject: true, // will inject the DLL bundles to index.html inject: true, // will inject the DLL bundles to index.html
@ -235,12 +240,7 @@ module.exports = function(opts = {}) {
debug: true, debug: true,
context: path.join(config.path.root), context: path.join(config.path.root),
entry: { entry: {
vendor: [ vendor: [...config.vendorModules, 'babel-polyfill', 'bootstrap-sass', 'font-awesome']
...config.vendorModules,
'babel-polyfill',
'bootstrap-sass',
'font-awesome'
]
} }
}), }),
new HardSourceWebpackPlugin({ new HardSourceWebpackPlugin({
@ -259,19 +259,21 @@ module.exports = function(opts = {}) {
if (options.isElectronBuild) { if (options.isElectronBuild) {
// target: 'electron-renderer' kills scrypt, so manually pull in some // target: 'electron-renderer' kills scrypt, so manually pull in some
// of its configuration instead // of its configuration instead
plugins.push(new webpack.ExternalsPlugin("commonjs", [ plugins.push(
"desktop-capturer", new webpack.ExternalsPlugin('commonjs', [
"electron", 'desktop-capturer',
"ipc", 'electron',
"ipc-renderer", 'ipc',
"remote", 'ipc-renderer',
"web-frame", 'remote',
"clipboard", 'web-frame',
"crash-reporter", 'clipboard',
"native-image", 'crash-reporter',
"screen", 'native-image',
"shell" 'screen',
])); 'shell'
])
);
} }
// ==================== // ====================
@ -281,8 +283,7 @@ module.exports = function(opts = {}) {
if (!options.isProduction) { if (!options.isProduction) {
if (process.env.SLOW_BUILD_SPEED) { if (process.env.SLOW_BUILD_SPEED) {
devtool = 'source-map'; devtool = 'source-map';
} } else {
else {
devtool = 'cheap-module-eval-source-map'; devtool = 'cheap-module-eval-source-map';
} }
} }
@ -295,11 +296,11 @@ module.exports = function(opts = {}) {
filename: options.isProduction ? '[name].[chunkhash:8].js' : '[name].js', filename: options.isProduction ? '[name].[chunkhash:8].js' : '[name].js',
publicPath: isDownloadable && options.isProduction ? './' : '/', publicPath: isDownloadable && options.isProduction ? './' : '/',
crossOriginLoading: 'anonymous' crossOriginLoading: 'anonymous'
} };
// The final bundle // The final bundle
return { return {
devtool,
entry, entry,
output, output,
module: { rules }, module: { rules },
@ -318,4 +319,4 @@ module.exports = function(opts = {}) {
modules: false modules: false
} }
}; };
} };