diff --git a/app/components/UI/ActionModal/__snapshots__/index.test.js.snap b/app/components/UI/ActionModal/__snapshots__/index.test.js.snap
index fb68323237f..3631e412687 100644
--- a/app/components/UI/ActionModal/__snapshots__/index.test.js.snap
+++ b/app/components/UI/ActionModal/__snapshots__/index.test.js.snap
@@ -53,10 +53,8 @@ exports[`ActionModal should render correctly 1`] = `
@@ -103,7 +103,7 @@ exports[`ActionModal should render correctly 1`] = `
testID=""
type="neutral"
>
- CANCEL
+ Cancel
- CONFIRM
+ Confirm
diff --git a/app/components/UI/ActionModal/index.js b/app/components/UI/ActionModal/index.js
index d71d3643108..d53a607343c 100644
--- a/app/components/UI/ActionModal/index.js
+++ b/app/components/UI/ActionModal/index.js
@@ -2,8 +2,9 @@ import React from 'react';
import PropTypes from 'prop-types';
import { StyleSheet, View } from 'react-native';
import Modal from 'react-native-modal';
-import { colors, baseStyles } from '../../../styles/common';
+import { colors } from '../../../styles/common';
import StyledButton from '../StyledButton';
+import { strings } from '../../../../locales/i18n';
const styles = StyleSheet.create({
modal: {
@@ -15,10 +16,8 @@ const styles = StyleSheet.create({
justifyContent: 'center',
alignItems: 'center'
},
- modalText: {
- alignSelf: 'center',
+ modalContainer: {
width: '90%',
- height: 300,
backgroundColor: colors.white,
borderRadius: 10
},
@@ -29,6 +28,11 @@ const styles = StyleSheet.create({
flexDirection: 'row',
padding: 16
},
+ childrenContainer: {
+ minHeight: 250,
+ flexDirection: 'row',
+ alignItems: 'center'
+ },
button: {
flex: 1
},
@@ -49,6 +53,7 @@ export default function ActionModal({
cancelText,
children,
confirmText,
+ confirmDisabled,
cancelButtonMode,
confirmButtonMode,
onCancelPress,
@@ -66,8 +71,8 @@ export default function ActionModal({
swipeDirection={'down'}
>
-
- {children}
+
+ {children}
- {cancelText.toUpperCase()}
+ {cancelText}
- {confirmText.toUpperCase()}
+ {confirmText}
@@ -97,8 +103,9 @@ ActionModal.defaultProps = {
confirmButtonMode: 'warning',
confirmTestID: '',
cancelTestID: '',
- cancelText: 'CANCEL',
- confirmText: 'CONFIRM'
+ cancelText: strings('action_view.cancel'),
+ confirmText: strings('action_view.confirm'),
+ confirmDisabled: false
};
ActionModal.propTypes = {
@@ -121,11 +128,15 @@ ActionModal.propTypes = {
/**
* Type of button to show as the cancel button
*/
- cancelButtonMode: PropTypes.oneOf(['cancel', 'neutral', 'confirm']),
+ cancelButtonMode: PropTypes.oneOf(['cancel', 'neutral', 'confirm', 'normal']),
/**
* Type of button to show as the confirm button
*/
confirmButtonMode: PropTypes.oneOf(['normal', 'confirm', 'warning']),
+ /**
+ * Whether confirm button is disabled
+ */
+ confirmDisabled: PropTypes.bool,
/**
* Text to show in the confirm button
*/
diff --git a/app/components/UI/TransactionElement/index.js b/app/components/UI/TransactionElement/index.js
index d7532a15baa..237c31e5311 100644
--- a/app/components/UI/TransactionElement/index.js
+++ b/app/components/UI/TransactionElement/index.js
@@ -608,14 +608,14 @@ class TransactionElement extends PureComponent {
const { tx } = this.props;
const existingGasPrice = tx.transaction ? tx.transaction.gasPrice : '0x0';
const existingGasPriceDecimal = parseInt(existingGasPrice === undefined ? '0x0' : existingGasPrice, 16);
- this.mounted && this.props.onCancelAction(true, existingGasPriceDecimal, this.props.tx.id);
+ this.mounted && this.props.onCancelAction(true, existingGasPriceDecimal, this.props.tx);
};
showSpeedUpModal = () => {
const { tx } = this.props;
const existingGasPrice = tx.transaction ? tx.transaction.gasPrice : '0x0';
const existingGasPriceDecimal = parseInt(existingGasPrice === undefined ? '0x0' : existingGasPrice, 16);
- this.mounted && this.props.onSpeedUpAction(true, existingGasPriceDecimal, this.props.tx.id);
+ this.mounted && this.props.onSpeedUpAction(true, existingGasPriceDecimal, this.props.tx);
};
hideSpeedUpModal = () => {
diff --git a/app/components/UI/Transactions/index.js b/app/components/UI/Transactions/index.js
index 070ec7cc186..6d344e4d5a7 100644
--- a/app/components/UI/Transactions/index.js
+++ b/app/components/UI/Transactions/index.js
@@ -23,6 +23,9 @@ import TransactionsNotificationManager from '../../../core/TransactionsNotificat
import ActionModal from '../ActionModal';
import { CANCEL_RATE, SPEED_UP_RATE } from 'gaba';
import { renderFromWei } from '../../../util/number';
+import { safeToChecksumAddress } from '../../../util/address';
+import { hexToBN } from 'gaba/dist/util';
+import { BN } from 'ethereumjs-util';
const styles = StyleSheet.create({
wrapper: {
@@ -54,7 +57,8 @@ const styles = StyleSheet.create({
modalText: {
...fontStyles.normal,
fontSize: 14,
- textAlign: 'center'
+ textAlign: 'center',
+ paddingVertical: 8
},
modalTitle: {
...fontStyles.bold,
@@ -64,7 +68,8 @@ const styles = StyleSheet.create({
gasTitle: {
...fontStyles.bold,
fontSize: 16,
- textAlign: 'center'
+ textAlign: 'center',
+ marginVertical: 8
},
cancelFeeWrapper: {
backgroundColor: colors.grey000,
@@ -75,6 +80,13 @@ const styles = StyleSheet.create({
...fontStyles.bold,
fontSize: 24,
textAlign: 'center'
+ },
+ warningText: {
+ ...fontStyles.normal,
+ fontSize: 12,
+ color: colors.red,
+ paddingVertical: 8,
+ textAlign: 'center'
}
});
@@ -85,6 +97,10 @@ const ROW_HEIGHT = (Platform.OS === 'ios' ? 95 : 100) + StyleSheet.hairlineWidth
*/
class Transactions extends PureComponent {
static propTypes = {
+ /**
+ * Map of accounts to information objects including balances
+ */
+ accounts: PropTypes.object,
/**
* Object containing token exchange rates in the format address => exchangeRate
*/
@@ -161,7 +177,9 @@ class Transactions extends PureComponent {
ready: false,
refreshing: false,
cancelIsOpen: false,
- speedUpIsOpen: false
+ cancelConfirmDisabled: false,
+ speedUpIsOpen: false,
+ speedUpConfirmDisabled: false
};
existingGasPriceDecimal = null;
@@ -265,10 +283,32 @@ class Transactions extends PureComponent {
blockExplorer = () => hasBlockExplorer(this.props.networkType);
- onSpeedUpAction = (speedUpAction, existingGasPriceDecimal, speedUpTxId) => {
+ validateBalance = (tx, rate) => {
+ const { accounts } = this.props;
+ try {
+ const checksummedFrom = safeToChecksumAddress(tx.transaction.from);
+ const balance = accounts[checksummedFrom].balance;
+ return hexToBN(balance).lt(
+ hexToBN(tx.transaction.gasPrice)
+ .mul(new BN(rate * 10))
+ .mul(new BN(10))
+ .mul(hexToBN(tx.transaction.gas))
+ .add(hexToBN(tx.transaction.value))
+ );
+ } catch (e) {
+ return false;
+ }
+ };
+
+ onSpeedUpAction = (speedUpAction, existingGasPriceDecimal, tx) => {
this.setState({ speedUpIsOpen: speedUpAction });
this.existingGasPriceDecimal = existingGasPriceDecimal;
- this.speedUpTxId = speedUpTxId;
+ this.speedUpTxId = tx.id;
+ if (this.validateBalance(tx, SPEED_UP_RATE)) {
+ this.setState({ speedUpIsOpen: speedUpAction, speedUpConfirmDisabled: true });
+ } else {
+ this.setState({ speedUpIsOpen: speedUpAction, speedUpConfirmDisabled: false });
+ }
};
onSpeedUpCompleted = () => {
@@ -277,12 +317,15 @@ class Transactions extends PureComponent {
this.speedUpTxId = null;
};
- onCancelAction = (cancelAction, existingGasPriceDecimal, cancelTxId) => {
- this.setState({ cancelIsOpen: cancelAction });
+ onCancelAction = (cancelAction, existingGasPriceDecimal, tx) => {
this.existingGasPriceDecimal = existingGasPriceDecimal;
- this.cancelTxId = cancelTxId;
+ this.cancelTxId = tx.id;
+ if (this.validateBalance(tx, SPEED_UP_RATE)) {
+ this.setState({ cancelIsOpen: cancelAction, cancelConfirmDisabled: true });
+ } else {
+ this.setState({ cancelIsOpen: cancelAction, cancelConfirmDisabled: false });
+ }
};
-
onCancelCompleted = () => {
this.setState({ cancelIsOpen: false });
this.existingGasPriceDecimal = null;
@@ -342,6 +385,7 @@ class Transactions extends PureComponent {
}
const { submittedTransactions, confirmedTransactions, header } = this.props;
+ const { cancelConfirmDisabled, speedUpConfirmDisabled } = this.state;
const transactions =
submittedTransactions && submittedTransactions.length
? submittedTransactions.concat(confirmedTransactions)
@@ -369,6 +413,7 @@ class Transactions extends PureComponent {
onCancelPress={this.onCancelCompleted}
onRequestClose={this.onCancelCompleted}
onConfirmPress={this.cancelTransaction}
+ confirmDisabled={cancelConfirmDisabled}
>
{strings('transaction.cancel_tx_title')}
@@ -381,6 +426,9 @@ class Transactions extends PureComponent {
{strings('transaction.cancel_tx_message')}
+ {cancelConfirmDisabled && (
+ {strings('transaction.insufficient')}
+ )}
@@ -391,6 +439,7 @@ class Transactions extends PureComponent {
onCancelPress={this.onSpeedUpCompleted}
onRequestClose={this.onSpeedUpCompleted}
onConfirmPress={this.speedUpTransaction}
+ confirmDisabled={speedUpConfirmDisabled}
>
{strings('transaction.speedup_tx_title')}
@@ -403,6 +452,9 @@ class Transactions extends PureComponent {
{strings('transaction.speedup_tx_message')}
+ {speedUpConfirmDisabled && (
+ {strings('transaction.insufficient')}
+ )}
@@ -411,6 +463,7 @@ class Transactions extends PureComponent {
}
const mapStateToProps = state => ({
+ accounts: state.engine.backgroundState.AccountTrackerController.accounts,
tokens: state.engine.backgroundState.AssetsController.tokens.reduce((tokens, token) => {
tokens[token.address] = token;
return tokens;
diff --git a/app/components/UI/Transactions/index.test.js b/app/components/UI/Transactions/index.test.js
index fd5991c320d..600eeb136cd 100644
--- a/app/components/UI/Transactions/index.test.js
+++ b/app/components/UI/Transactions/index.test.js
@@ -12,6 +12,9 @@ describe('Transactions', () => {
const initialState = {
engine: {
backgroundState: {
+ AccountTrackerController: {
+ accounts: {}
+ },
AssetsController: {
tokens: []
},
diff --git a/app/components/Views/Settings/AdvancedSettings/__snapshots__/index.test.js.snap b/app/components/Views/Settings/AdvancedSettings/__snapshots__/index.test.js.snap
index cd20be531ef..5d210e50950 100644
--- a/app/components/Views/Settings/AdvancedSettings/__snapshots__/index.test.js.snap
+++ b/app/components/Views/Settings/AdvancedSettings/__snapshots__/index.test.js.snap
@@ -43,6 +43,7 @@ exports[`AdvancedSettings should render correctly 1`] = `
cancelTestID=""
cancelText="NEVERMIND"
confirmButtonMode="warning"
+ confirmDisabled={false}
confirmTestID=""
confirmText="RESET"
modalVisible={false}
diff --git a/app/components/Views/Settings/SecuritySettings/__snapshots__/index.test.js.snap b/app/components/Views/Settings/SecuritySettings/__snapshots__/index.test.js.snap
index e31b33df81c..7febeecbe87 100644
--- a/app/components/Views/Settings/SecuritySettings/__snapshots__/index.test.js.snap
+++ b/app/components/Views/Settings/SecuritySettings/__snapshots__/index.test.js.snap
@@ -472,6 +472,7 @@ exports[`SecuritySettings should render correctly 1`] = `
cancelTestID=""
cancelText="NEVERMIND"
confirmButtonMode="warning"
+ confirmDisabled={false}
confirmTestID=""
confirmText="CLEAR"
modalVisible={false}
@@ -522,6 +523,7 @@ exports[`SecuritySettings should render correctly 1`] = `
cancelTestID=""
cancelText="NEVERMIND"
confirmButtonMode="warning"
+ confirmDisabled={false}
confirmTestID=""
confirmText="CLEAR"
modalVisible={false}
diff --git a/package.json b/package.json
index 1a1361839a8..9a482a5df4e 100644
--- a/package.json
+++ b/package.json
@@ -80,7 +80,7 @@
"ethjs-unit": "0.1.6",
"events": "3.0.0",
"fuse.js": "3.4.4",
- "gaba": "1.9.0",
+ "gaba": "1.9.2",
"https-browserify": "0.0.1",
"lottie-react-native": "git+ssh://git@github.com/brunobar79/lottie-react-native.git#7ce6a78ac4ac7b9891bc513cb3f12f8b9c9d9106",
"multihashes": "0.4.14",
diff --git a/scripts/build.sh b/scripts/build.sh
index dcfa475f79b..4031f84d8c8 100755
--- a/scripts/build.sh
+++ b/scripts/build.sh
@@ -139,7 +139,7 @@ buildAndroid(){
buildIosSimulator(){
prebuild_ios
- react-native run-ios --simulator "iPhone 11 Pro (13.2)"
+ react-native run-ios --simulator "iPhone 11 Pro (13.3)"
}
buildIosDevice(){
diff --git a/yarn.lock b/yarn.lock
index fb0bca12acb..189f6f4b453 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -4742,10 +4742,10 @@ g-status@^2.0.2:
matcher "^1.0.0"
simple-git "^1.85.0"
-gaba@1.9.0:
- version "1.9.0"
- resolved "https://registry.yarnpkg.com/gaba/-/gaba-1.9.0.tgz#ccd9f99c56687b5acd39f9e3ceb435b2a59b6aa1"
- integrity sha512-HoVreAdZssL0jNHuzZ7WP+YKZ0riu44jVDWxhQ9hsgPuzxbVEsz9fO/HDxqAdNZS1Cswayq6+ciZ3HSCFWMKbQ==
+gaba@1.9.2:
+ version "1.9.2"
+ resolved "https://registry.yarnpkg.com/gaba/-/gaba-1.9.2.tgz#546b3733f686561032004f589ff9da6b11d02000"
+ integrity sha512-qVcSkRBErmYkiAxy/VGqetTF2zNrc2+dsv7SBcHVt5jYikhw2EMdfsEwEyWsjAM/YGry7mxmozlIS7nvdJXnYQ==
dependencies:
await-semaphore "^0.1.3"
eth-contract-metadata "^1.11.0"