From aa08171c722f6afbd01afd799078f497b64f853f Mon Sep 17 00:00:00 2001 From: David Walsh Date: Mon, 2 Aug 2021 21:08:12 -0500 Subject: [PATCH 01/55] EIP-1559 - Return null from GasTiming if on non-1559 network (#11733) --- ui/components/app/gas-timing/gas-timing.component.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/components/app/gas-timing/gas-timing.component.js b/ui/components/app/gas-timing/gas-timing.component.js index edea0a924..5b93c552a 100644 --- a/ui/components/app/gas-timing/gas-timing.component.js +++ b/ui/components/app/gas-timing/gas-timing.component.js @@ -168,6 +168,6 @@ export default function GasTiming({ } GasTiming.propTypes = { - maxPriorityFeePerGas: PropTypes.string.isRequired, - maxFeePerGas: PropTypes.string.isRequired, + maxPriorityFeePerGas: PropTypes.string, + maxFeePerGas: PropTypes.string, }; From cdbfe0f1329680ca3e12b218e8c319394b8e509e Mon Sep 17 00:00:00 2001 From: David Walsh Date: Mon, 2 Aug 2021 21:08:20 -0500 Subject: [PATCH 02/55] EIP-1559 - Ensure transaction detail font-size and icon colors are consistent with Figma design (#11735) --- .../app/advanced-gas-controls/index.scss | 2 +- .../app/transaction-detail-item/index.scss | 4 ++ .../transaction-detail-item.component.js | 6 +- .../confirm-transaction-base.component.js | 59 +++++++++++-------- 4 files changed, 41 insertions(+), 30 deletions(-) diff --git a/ui/components/app/advanced-gas-controls/index.scss b/ui/components/app/advanced-gas-controls/index.scss index 7fdf9174f..969c8ca3f 100644 --- a/ui/components/app/advanced-gas-controls/index.scss +++ b/ui/components/app/advanced-gas-controls/index.scss @@ -30,6 +30,6 @@ } path { - fill: #dadada; + fill: $ui-3; } } diff --git a/ui/components/app/transaction-detail-item/index.scss b/ui/components/app/transaction-detail-item/index.scss index 30308cdf7..a2dc8e4cd 100644 --- a/ui/components/app/transaction-detail-item/index.scss +++ b/ui/components/app/transaction-detail-item/index.scss @@ -14,6 +14,10 @@ .info-tooltip { display: inline-block; margin-inline-start: 4px; + + path { + fill: $ui-3; + } } &__total { diff --git a/ui/components/app/transaction-detail-item/transaction-detail-item.component.js b/ui/components/app/transaction-detail-item/transaction-detail-item.component.js index d8f6cf299..80a3e2fef 100644 --- a/ui/components/app/transaction-detail-item/transaction-detail-item.component.js +++ b/ui/components/app/transaction-detail-item/transaction-detail-item.component.js @@ -22,14 +22,14 @@ export default function TransactionDetailItem({ {detailTitle} {detailText && ( @@ -39,7 +39,7 @@ export default function TransactionDetailItem({ {detailTotal} diff --git a/ui/pages/confirm-transaction-base/confirm-transaction-base.component.js b/ui/pages/confirm-transaction-base/confirm-transaction-base.component.js index 415c1cd05..96cf67903 100644 --- a/ui/pages/confirm-transaction-base/confirm-transaction-base.component.js +++ b/ui/pages/confirm-transaction-base/confirm-transaction-base.component.js @@ -331,7 +331,6 @@ export default class ConfirmTransactionBase extends Component { @@ -379,17 +378,21 @@ export default class ConfirmTransactionBase extends Component { hideLabel /> } - subText={t('editGasSubTextFee', [ - , - ])} + subText={ + + {t('editGasSubTextFee', [ + , + ])} + + } subTitle={ , - ])} + subText={ + + {t('editGasSubTextAmount', [ + , + ])} + + } />, ]} /> From 7ac1972371e8e85efdbd09cbb87c2c7d6cc929a3 Mon Sep 17 00:00:00 2001 From: Dan J Miller Date: Mon, 2 Aug 2021 23:39:21 -0230 Subject: [PATCH 03/55] Ensure that gas price in popover updates when api provided estimate updates (#11727) --- ui/hooks/useGasFeeInputs.js | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/ui/hooks/useGasFeeInputs.js b/ui/hooks/useGasFeeInputs.js index 4204a702d..4d4f583fc 100644 --- a/ui/hooks/useGasFeeInputs.js +++ b/ui/hooks/useGasFeeInputs.js @@ -1,8 +1,7 @@ import { addHexPrefix } from 'ethereumjs-util'; import { useCallback, useState } from 'react'; import { useSelector } from 'react-redux'; -import { findKey } from 'lodash'; - +import { findKey, isEqual } from 'lodash'; import { GAS_ESTIMATE_TYPES, EDIT_GAS_MODES, @@ -220,6 +219,9 @@ export function useGasFeeInputs( ? Number(hexWEIToDecGWEI(transaction.txParams.maxPriorityFeePerGas)) : null, ); + const [gasPriceHasBeenManuallySet, setGasPriceHasBeenManuallySet] = useState( + false, + ); const [gasPrice, setGasPrice] = useState( transaction?.txParams?.gasPrice ? Number(hexWEIToDecGWEI(transaction.txParams.gasPrice)) @@ -263,9 +265,20 @@ export function useGasFeeInputs( estimateToUse, ); + const [initialGasPriceEstimates] = useState(gasFeeEstimates); + const gasPriceEstimatesHaveNotChanged = isEqual( + initialGasPriceEstimates, + gasFeeEstimates, + ); const gasPriceToUse = - gasPrice ?? - getGasPriceEstimate(gasFeeEstimates, gasEstimateType, estimateToUse); + gasPrice !== null && + (gasPriceHasBeenManuallySet || gasPriceEstimatesHaveNotChanged) + ? gasPrice + : getGasPriceEstimate( + gasFeeEstimates, + gasEstimateType, + estimateToUse || defaultEstimateToUse, + ); // We have two helper methods that take an object that can have either // gasPrice OR the EIP-1559 fields on it, plus gasLimit. This object is @@ -456,6 +469,7 @@ export function useGasFeeInputs( setMaxFeePerGas(null); setMaxPriorityFeePerGas(null); setGasPrice(null); + setGasPriceHasBeenManuallySet(false); }, [minimumGasLimit, gasErrors.gasLimit, transaction], ); @@ -489,6 +503,7 @@ export function useGasFeeInputs( setGasLimit(gasLimit); setMaxFeePerGas(maxFeePerGasToUse); setMaxPriorityFeePerGas(maxPriorityFeePerGasToUse); + setGasPriceHasBeenManuallySet(true); }, balanceError, estimatesUnavailableWarning, From 9ccb91f47b4e33dd98c97ac9d4409e3cefa6d77e Mon Sep 17 00:00:00 2001 From: David Walsh Date: Tue, 3 Aug 2021 10:52:45 -0500 Subject: [PATCH 04/55] EIP-1559 - Fix education modal typo (#11740) --- app/_locales/en/messages.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index 74e74f5ff..4c7776fa3 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -633,7 +633,7 @@ "message": "This is best for swaps or other time sensitive transactions. If a swap takes too long to process it will often fail and you may lose funds." }, "editGasEducationLowExplanation": { - "message": "Low A lower gas fee should only be selected for transactions where processing time is less important. With a lower fee, it can be be hard to predict when (or if) your transaction with be successful." + "message": "A lower gas fee should only be selected for transactions where processing time is less important. With a lower fee, it can be be hard to predict when (or if) your transaction with be successful." }, "editGasEducationMediumExplanation": { "message": "A medium gas fee is good for sending, withdrawing or other non-time sensitive but important transactions." From fba662ca2b8b6bf68c02c268ea84cdc4e535306e Mon Sep 17 00:00:00 2001 From: David Walsh Date: Tue, 3 Aug 2021 10:54:49 -0500 Subject: [PATCH 05/55] EIP-1559 - Provide better validation for gas price and gas limit (#11736) --- app/_locales/en/messages.json | 3 +++ .../advanced-gas-controls/advanced-gas-controls.component.js | 5 +++++ ui/components/ui/numeric-input/numeric-input.component.js | 2 +- ui/helpers/constants/gas.js | 3 +++ ui/hooks/useGasFeeInputs.js | 3 +++ 5 files changed, 15 insertions(+), 1 deletion(-) diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index 4c7776fa3..1c21f2a93 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -683,6 +683,9 @@ "editGasMedium": { "message": "Medium" }, + "editGasPriceTooLow": { + "message": "Gas price must be greater than 0" + }, "editGasPriceTooltip": { "message": "This network requires a “Gas price” field when submitting a transaction. Gas price is the amount you will pay pay per unit of gas." }, diff --git a/ui/components/app/advanced-gas-controls/advanced-gas-controls.component.js b/ui/components/app/advanced-gas-controls/advanced-gas-controls.component.js index 4e00ebe0c..5cc473f1b 100644 --- a/ui/components/app/advanced-gas-controls/advanced-gas-controls.component.js +++ b/ui/components/app/advanced-gas-controls/advanced-gas-controls.component.js @@ -156,6 +156,11 @@ export default function AdvancedGasControls({ ) } + error={ + gasErrors?.gasPrice + ? getGasFormErrorText(gasErrors.gasPrice, t) + : null + } /> )} diff --git a/ui/components/ui/numeric-input/numeric-input.component.js b/ui/components/ui/numeric-input/numeric-input.component.js index cc8e024bf..9aa633692 100644 --- a/ui/components/ui/numeric-input/numeric-input.component.js +++ b/ui/components/ui/numeric-input/numeric-input.component.js @@ -25,7 +25,7 @@ export default function NumericInput({ } }} onChange={(e) => { - onChange?.(parseFloat(e.target.value, 10)); + onChange?.(parseFloat(e.target.value || 0, 10)); }} min="0" autoFocus={autoFocus} diff --git a/ui/helpers/constants/gas.js b/ui/helpers/constants/gas.js index f8552fd65..53ffcf681 100644 --- a/ui/helpers/constants/gas.js +++ b/ui/helpers/constants/gas.js @@ -6,6 +6,7 @@ export const GAS_FORM_ERRORS = { MAX_PRIORITY_FEE_HIGH_WARNING: 'editGasMaxPriorityFeeHigh', MAX_FEE_HIGH_WARNING: 'editGasMaxFeeHigh', MAX_FEE_IMBALANCE: 'editGasMaxFeeImbalance', + GAS_PRICE_TOO_LOW: 'editGasPriceTooLow', }; export function getGasFormErrorText(type, t, { minimumGasLimit } = {}) { @@ -24,6 +25,8 @@ export function getGasFormErrorText(type, t, { minimumGasLimit } = {}) { return t('editGasMaxFeeHigh'); case GAS_FORM_ERRORS.MAX_FEE_IMBALANCE: return t('editGasMaxFeePriorityImbalance'); + case GAS_FORM_ERRORS.GAS_PRICE_TOO_LOW: + return t('editGasPriceTooLow'); default: return ''; } diff --git a/ui/hooks/useGasFeeInputs.js b/ui/hooks/useGasFeeInputs.js index 4d4f583fc..96e49031a 100644 --- a/ui/hooks/useGasFeeInputs.js +++ b/ui/hooks/useGasFeeInputs.js @@ -425,6 +425,9 @@ export function useGasFeeInputs( if (networkAndAccountSupports1559) { estimatesUnavailableWarning = true; } + if (gasPriceToUse <= 0) { + gasErrors.gasPrice = GAS_FORM_ERRORS.GAS_PRICE_TOO_LOW; + } break; default: break; From a4927a30ace01ebf133fdc72e66a97036b2abac2 Mon Sep 17 00:00:00 2001 From: Alex Donesky Date: Tue, 3 Aug 2021 10:56:49 -0500 Subject: [PATCH 06/55] Add gasPrice to be used on non-1559 networks for transaction details on confirm screen (#11741) --- .../confirm-transaction-base.component.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/ui/pages/confirm-transaction-base/confirm-transaction-base.component.js b/ui/pages/confirm-transaction-base/confirm-transaction-base.component.js index 96cf67903..35a855e47 100644 --- a/ui/pages/confirm-transaction-base/confirm-transaction-base.component.js +++ b/ui/pages/confirm-transaction-base/confirm-transaction-base.component.js @@ -385,7 +385,9 @@ export default class ConfirmTransactionBase extends Component { key="gas-subtext" type={SECONDARY} value={getHexGasTotal({ - gasPrice: txData.txParams.maxFeePerGas, + gasPrice: + txData.txParams.maxFeePerGas ?? + txData.txParams.gasPrice, gasLimit: txData.txParams.gas, })} hideLabel @@ -430,7 +432,9 @@ export default class ConfirmTransactionBase extends Component { value={addHexes( txData.txParams.value, getHexGasTotal({ - gasPrice: txData.txParams.maxFeePerGas, + gasPrice: + txData.txParams.maxFeePerGas ?? + txData.txParams.gasPrice, gasLimit: txData.txParams.gas, }), )} From deae2d47bf7f8e8ebff040e6432e00915f1e218a Mon Sep 17 00:00:00 2001 From: Daniel <80175477+dan437@users.noreply.github.com> Date: Tue, 3 Aug 2021 18:10:12 +0200 Subject: [PATCH 07/55] Fix a position of a tooltip icon (#11739) --- ui/components/ui/info-tooltip/index.scss | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ui/components/ui/info-tooltip/index.scss b/ui/components/ui/info-tooltip/index.scss index 335108ffc..f96dc943c 100644 --- a/ui/components/ui/info-tooltip/index.scss +++ b/ui/components/ui/info-tooltip/index.scss @@ -3,6 +3,12 @@ height: 12px; width: 12px; } + + &__tooltip-container { + svg { + position: static; + } + } } .tippy-popper[x-placement^=top] .tippy-tooltip-info-theme [x-arrow], From 24d563fc2c1e1e5ccf8de186cb343335f04ffc35 Mon Sep 17 00:00:00 2001 From: David Walsh Date: Tue, 3 Aug 2021 12:51:02 -0500 Subject: [PATCH 08/55] EIP-1559 - Show minium native currency in banner when on testnets (#11743) --- .../app/edit-gas-display/edit-gas-display.component.js | 6 ++++-- .../app/edit-gas-popover/edit-gas-popover.component.js | 2 ++ ui/hooks/useGasFeeInputs.js | 6 ++++++ 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/ui/components/app/edit-gas-display/edit-gas-display.component.js b/ui/components/app/edit-gas-display/edit-gas-display.component.js index 203457583..66b7dd9bb 100644 --- a/ui/components/app/edit-gas-display/edit-gas-display.component.js +++ b/ui/components/app/edit-gas-display/edit-gas-display.component.js @@ -45,6 +45,7 @@ export default function EditGasDisplay({ setMaxFeePerGas, maxFeePerGasFiat, estimatedMaximumNative, + estimatedMinimumNative, isGasEstimatesLoading, gasFeeEstimates, gasEstimateType, @@ -140,9 +141,9 @@ export default function EditGasDisplay({ )} Date: Tue, 3 Aug 2021 12:09:39 -0700 Subject: [PATCH 09/55] Fall back to aggregator name when icon is unavailable (Swaps Load View) (#11718) --- .../loading-swaps-quotes/aggregator-logo.js | 31 ++++++++++++------- .../swaps/loading-swaps-quotes/index.scss | 5 +++ .../loading-swaps-quotes.js | 3 +- 3 files changed, 27 insertions(+), 12 deletions(-) diff --git a/ui/pages/swaps/loading-swaps-quotes/aggregator-logo.js b/ui/pages/swaps/loading-swaps-quotes/aggregator-logo.js index 2d43d83a6..395389ae5 100644 --- a/ui/pages/swaps/loading-swaps-quotes/aggregator-logo.js +++ b/ui/pages/swaps/loading-swaps-quotes/aggregator-logo.js @@ -10,22 +10,31 @@ function hexToRGB(hex, alpha) { return `rgba(${r}, ${g}, ${b}, ${alpha})`; } -export default function AggregatorLogo({ icon, color }) { +export default function AggregatorLogo({ name, icon, color }) { return (
-
- -
+ {icon && color ? ( +
+ +
+ ) : ( + name && ( +
+ {name} +
+ ) + )}
); } AggregatorLogo.propTypes = { - icon: PropTypes.string.isRequired, - color: PropTypes.string.isRequired, + name: PropTypes.string, + icon: PropTypes.string, + color: PropTypes.string, }; diff --git a/ui/pages/swaps/loading-swaps-quotes/index.scss b/ui/pages/swaps/loading-swaps-quotes/index.scss index 9c70f0503..e9b21bf0e 100644 --- a/ui/pages/swaps/loading-swaps-quotes/index.scss +++ b/ui/pages/swaps/loading-swaps-quotes/index.scss @@ -107,12 +107,17 @@ display: flex; justify-content: center; align-items: center; + background: $ui-black; } img { width: 74px; height: 30px; } + + span { + color: $ui-white; + } } &__loading-bar-container { diff --git a/ui/pages/swaps/loading-swaps-quotes/loading-swaps-quotes.js b/ui/pages/swaps/loading-swaps-quotes/loading-swaps-quotes.js index b0a5760f4..1e8d389f6 100644 --- a/ui/pages/swaps/loading-swaps-quotes/loading-swaps-quotes.js +++ b/ui/pages/swaps/loading-swaps-quotes/loading-swaps-quotes.js @@ -220,7 +220,7 @@ export default function LoadingSwapsQuotes({ key={`aggregator-logo-${aggName}`} > @@ -245,6 +245,7 @@ LoadingSwapsQuotes.propTypes = { onDone: PropTypes.func.isRequired, aggregatorMetadata: PropTypes.objectOf( PropTypes.shape({ + title: PropTypes.string, color: PropTypes.string, icon: PropTypes.string, }), From 615380b76dcd7c846282036f7565d6df95d05f12 Mon Sep 17 00:00:00 2001 From: Alex Donesky Date: Tue, 3 Aug 2021 14:34:24 -0500 Subject: [PATCH 10/55] Remove estimate detail above advanced gas controls in non-1559 network (#11744) * Remove estimate detail above advanced gas controls in non-1559 network * lint * remove gasEstimate localization text --- app/_locales/en/messages.json | 3 --- .../advanced-gas-controls.component.js | 27 ------------------- .../app/advanced-gas-controls/index.scss | 9 ------- 3 files changed, 39 deletions(-) diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index 1c21f2a93..f4d28ac59 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -907,9 +907,6 @@ "gasEstimatesUnavailableWarning": { "message": "Our low, medium and high estimates are not available." }, - "gasFeeEstimate": { - "message": "Estimate" - }, "gasLimit": { "message": "Gas Limit" }, diff --git a/ui/components/app/advanced-gas-controls/advanced-gas-controls.component.js b/ui/components/app/advanced-gas-controls/advanced-gas-controls.component.js index 5cc473f1b..e46560131 100644 --- a/ui/components/app/advanced-gas-controls/advanced-gas-controls.component.js +++ b/ui/components/app/advanced-gas-controls/advanced-gas-controls.component.js @@ -3,12 +3,6 @@ import PropTypes from 'prop-types'; import { useSelector } from 'react-redux'; import { I18nContext } from '../../../contexts/i18n'; -import Typography from '../../ui/typography/typography'; -import { - FONT_WEIGHT, - TYPOGRAPHY, - COLORS, -} from '../../../helpers/constants/design-system'; import FormField from '../../ui/form-field'; import { GAS_ESTIMATE_TYPES, @@ -135,27 +129,6 @@ export default function AdvancedGasControls({ tooltipText={t('editGasPriceTooltip')} value={gasPrice} numeric - titleDetail={ - suggestedValues.gasPrice && ( - <> - - {t('gasFeeEstimate')}: - {' '} - - {suggestedValues.gasPrice} - - - ) - } error={ gasErrors?.gasPrice ? getGasFormErrorText(gasErrors.gasPrice, t) diff --git a/ui/components/app/advanced-gas-controls/index.scss b/ui/components/app/advanced-gas-controls/index.scss index 969c8ca3f..1c149a225 100644 --- a/ui/components/app/advanced-gas-controls/index.scss +++ b/ui/components/app/advanced-gas-controls/index.scss @@ -3,19 +3,10 @@ margin-bottom: 20px; } - &__row-heading { - display: flex; - } - .info-tooltip { display: inline-block; } - &__row-heading-detail { - flex-grow: 1; - align-self: center; - } - .form-field__row--error .form-field__heading-title h6 { color: $error-1; From a4dda7d79ea9932cbd4af30f64401eba0af6d07e Mon Sep 17 00:00:00 2001 From: Alex Donesky Date: Tue, 3 Aug 2021 17:04:10 -0500 Subject: [PATCH 11/55] Fix ipfs dependency vulernability (#11745) --- package.json | 1 + yarn.lock | 43 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 848c2ddf3..9bca6ae41 100644 --- a/package.json +++ b/package.json @@ -82,6 +82,7 @@ "3box/ipfs/ipld-zcash/zcash-bitcore-lib/lodash": "^4.17.21", "3box/ipfs/ipld-zcash/zcash-bitcore-lib/elliptic": "^6.5.4", "3box/ipfs/libp2p-mdns/multicast-dns/dns-packet": "^5.2.2", + "3box/ipfs/prometheus-gc-stats/gc-stats/node-pre-gyp/tar": "^6.1.2", "3box/**/libp2p-crypto/node-forge": "^0.10.0", "3box/**/libp2p-keychain/node-forge": "^0.10.0", "analytics-node/axios": "^0.21.1", diff --git a/yarn.lock b/yarn.lock index d7bb3e8e0..c683c8251 100644 --- a/yarn.lock +++ b/yarn.lock @@ -19322,6 +19322,14 @@ minipass@^2.2.1, minipass@^2.6.4: safe-buffer "^5.1.2" yallist "^3.0.0" +minipass@^2.8.6: + version "2.9.0" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.9.0.tgz#e713762e7d3e32fed803115cf93e04bca9fcc9a6" + integrity sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg== + dependencies: + safe-buffer "^5.1.2" + yallist "^3.0.0" + minipass@^3.0.0, minipass@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.1.1.tgz#7607ce778472a185ad6d89082aa2070f79cedcd5" @@ -19344,6 +19352,14 @@ minizlib@^2.1.0: minipass "^3.0.0" yallist "^4.0.0" +minizlib@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" + integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== + dependencies: + minipass "^3.0.0" + yallist "^4.0.0" + mississippi@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/mississippi/-/mississippi-3.0.0.tgz#ea0a3291f97e0b5e8776b363d5f0a12d94c67022" @@ -26314,7 +26330,20 @@ tar@6.0.2, tar@^6.0.2: mkdirp "^1.0.3" yallist "^4.0.0" -tar@^4, tar@^4.0.2: +tar@^4: + version "4.4.15" + resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.15.tgz#3caced4f39ebd46ddda4d6203d48493a919697f8" + integrity sha512-ItbufpujXkry7bHH9NpQyTXPbJ72iTlXgkBAYsAjDXk3Ds8t/3NfO5P4xZGy7u+sYuQUbimgzswX4uQIEeNVOA== + dependencies: + chownr "^1.1.1" + fs-minipass "^1.2.5" + minipass "^2.8.6" + minizlib "^1.2.1" + mkdirp "^0.5.0" + safe-buffer "^5.1.2" + yallist "^3.0.3" + +tar@^4.0.2: version "4.4.11" resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.11.tgz#7ac09801445a3cf74445ed27499136b5240ffb73" integrity sha512-iI4zh3ktLJKaDNZKZc+fUONiQrSn9HkCFzamtb7k8FFmVilHVob7QsLX/VySAW8lAviMzMbFw4QtFb4errwgYA== @@ -26327,6 +26356,18 @@ tar@^4, tar@^4.0.2: safe-buffer "^5.1.2" yallist "^3.0.3" +tar@^6.1.2: + version "6.1.3" + resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.3.tgz#e44b97ee7d6cc7a4c574e8b01174614538291825" + integrity sha512-3rUqwucgVZXTeyJyL2jqtUau8/8r54SioM1xj3AmTX3HnWQdj2AydfJ2qYYayPyIIznSplcvU9mhBb7dR2XF3w== + dependencies: + chownr "^2.0.0" + fs-minipass "^2.0.0" + minipass "^3.0.0" + minizlib "^2.1.1" + mkdirp "^1.0.3" + yallist "^4.0.0" + tcp-port-used@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/tcp-port-used/-/tcp-port-used-1.0.1.tgz#46061078e2d38c73979a2c2c12b5a674e6689d70" From 1acbb39dffcb2b32baf77f6cfa7e74487afb6a75 Mon Sep 17 00:00:00 2001 From: Dan J Miller Date: Wed, 4 Aug 2021 10:52:54 -0230 Subject: [PATCH 12/55] Rename effective gas price field to total gas fee (#11754) --- app/_locales/en/messages.json | 6 +++--- .../transaction-breakdown.component.js | 4 +--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index f4d28ac59..f9be7c085 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -2539,12 +2539,12 @@ "transactionHistoryBaseFee": { "message": "Base fee (GWEI)" }, - "transactionHistoryEffectiveGasPrice": { - "message": "Effective gas price" - }, "transactionHistoryPriorityFee": { "message": "Priority fee (GWEI)" }, + "transactionHistoryTotalGasFee": { + "message": "Total Gas Fee" + }, "transactionResubmitted": { "message": "Transaction resubmitted with gas fee increased to $1 at $2" }, diff --git a/ui/components/app/transaction-breakdown/transaction-breakdown.component.js b/ui/components/app/transaction-breakdown/transaction-breakdown.component.js index 26ca79451..6f1f5ab9c 100644 --- a/ui/components/app/transaction-breakdown/transaction-breakdown.component.js +++ b/ui/components/app/transaction-breakdown/transaction-breakdown.component.js @@ -149,9 +149,7 @@ export default class TransactionBreakdown extends PureComponent { )} {isEIP1559Transaction && ( - + Date: Wed, 4 Aug 2021 10:53:19 -0230 Subject: [PATCH 13/55] Allows users to set a max priority fee below suggested, just showing a warning in that case (#11750) --- app/_locales/en/messages.json | 2 +- ui/hooks/useGasFeeInputs.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index f9be7c085..9a944df83 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -672,7 +672,7 @@ "message": "Max priority fee is higher than necessary. You may pay more than needed." }, "editGasMaxPriorityFeeLow": { - "message": "Max priority fee too low for network conditions" + "message": "Max priority fee extremely low for network conditions" }, "editGasMaxPriorityFeeTooltip": { "message": "Max priority fee (aka “miner tip”) goes directly to miners and incentivizes them to prioritize your transaction. You’ll most often pay your max setting" diff --git a/ui/hooks/useGasFeeInputs.js b/ui/hooks/useGasFeeInputs.js index 9dd8372ee..0aff8bbb0 100644 --- a/ui/hooks/useGasFeeInputs.js +++ b/ui/hooks/useGasFeeInputs.js @@ -397,7 +397,7 @@ export function useGasFeeInputs( maxPriorityFeePerGasToUse < gasFeeEstimates?.low?.suggestedMaxPriorityFeePerGas ) { - gasErrors.maxPriorityFee = GAS_FORM_ERRORS.MAX_PRIORITY_FEE_TOO_LOW; + gasWarnings.maxPriorityFee = GAS_FORM_ERRORS.MAX_PRIORITY_FEE_TOO_LOW; } else if (maxPriorityFeePerGasToUse >= maxFeePerGasToUse) { gasErrors.maxFee = GAS_FORM_ERRORS.MAX_FEE_IMBALANCE; } else if ( From b9fb495c7bd173ce0b1822cf4a8565c875b3b080 Mon Sep 17 00:00:00 2001 From: Dan J Miller Date: Wed, 4 Aug 2021 10:53:52 -0230 Subject: [PATCH 14/55] Allow max priority fee to be below 1, and only require it to be greater than 0 (#11749) --- app/_locales/en/messages.json | 6 +++--- ui/helpers/constants/gas.js | 4 ++-- ui/hooks/useGasFeeInputs.js | 5 +++-- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index 9a944df83..5f4efe357 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -668,6 +668,9 @@ "editGasMaxFeeTooltip": { "message": "The max fee is the most you’ll pay (base fee + priority fee)." }, + "editGasMaxPriorityFeeBelowMinimum": { + "message": "Max priority fee must be greater than 0 GWEI" + }, "editGasMaxPriorityFeeHigh": { "message": "Max priority fee is higher than necessary. You may pay more than needed." }, @@ -677,9 +680,6 @@ "editGasMaxPriorityFeeTooltip": { "message": "Max priority fee (aka “miner tip”) goes directly to miners and incentivizes them to prioritize your transaction. You’ll most often pay your max setting" }, - "editGasMaxPriorityFeeZeroError": { - "message": "Max priority fee must be at least 1 GWEI" - }, "editGasMedium": { "message": "Medium" }, diff --git a/ui/helpers/constants/gas.js b/ui/helpers/constants/gas.js index 53ffcf681..ae232c8a7 100644 --- a/ui/helpers/constants/gas.js +++ b/ui/helpers/constants/gas.js @@ -17,8 +17,8 @@ export function getGasFormErrorText(type, t, { minimumGasLimit } = {}) { return t('editGasMaxPriorityFeeLow'); case GAS_FORM_ERRORS.MAX_FEE_TOO_LOW: return t('editGasMaxFeeLow'); - case GAS_FORM_ERRORS.MAX_PRIORITY_FEE_ZERO: - return t('editGasMaxPriorityFeeZeroError'); + case GAS_FORM_ERRORS.MAX_PRIORITY_FEE_BELOW_MINIMUM: + return t('editGasMaxPriorityFeeBelowMinimum'); case GAS_FORM_ERRORS.MAX_PRIORITY_FEE_HIGH_WARNING: return t('editGasMaxPriorityFeeHigh'); case GAS_FORM_ERRORS.MAX_FEE_HIGH_WARNING: diff --git a/ui/hooks/useGasFeeInputs.js b/ui/hooks/useGasFeeInputs.js index 0aff8bbb0..eb746c167 100644 --- a/ui/hooks/useGasFeeInputs.js +++ b/ui/hooks/useGasFeeInputs.js @@ -390,8 +390,9 @@ export function useGasFeeInputs( switch (gasEstimateType) { case GAS_ESTIMATE_TYPES.FEE_MARKET: - if (maxPriorityFeePerGasToUse < 1) { - gasErrors.maxPriorityFee = GAS_FORM_ERRORS.MAX_PRIORITY_FEE_ZERO; + if (maxPriorityFeePerGasToUse <= 0) { + gasErrors.maxPriorityFee = + GAS_FORM_ERRORS.MAX_PRIORITY_FEE_BELOW_MINIMUM; } else if ( !isGasEstimatesLoading && maxPriorityFeePerGasToUse < From 7902e62d8fbe435964efb0d458a4fec93da07bd8 Mon Sep 17 00:00:00 2001 From: ryanml Date: Wed, 4 Aug 2021 06:24:07 -0700 Subject: [PATCH 15/55] Fixing errors in EditGasDisplay (#11748) --- .../app/edit-gas-display/edit-gas-display.component.js | 4 ++-- ui/components/ui/radio-group/radio-group.component.js | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/ui/components/app/edit-gas-display/edit-gas-display.component.js b/ui/components/app/edit-gas-display/edit-gas-display.component.js index 66b7dd9bb..306987118 100644 --- a/ui/components/app/edit-gas-display/edit-gas-display.component.js +++ b/ui/components/app/edit-gas-display/edit-gas-display.component.js @@ -262,7 +262,7 @@ EditGasDisplay.propTypes = { maxFeePerGasFiat: PropTypes.string, estimatedMaximumNative: PropTypes.string, estimatedMinimumNative: PropTypes.string, - isGasEstimatesLoading: PropTypes.boolean, + isGasEstimatesLoading: PropTypes.bool, gasFeeEstimates: PropTypes.object, gasEstimateType: PropTypes.string, gasPrice: PropTypes.string, @@ -273,7 +273,7 @@ EditGasDisplay.propTypes = { setEstimateToUse: PropTypes.func, estimatedMinimumFiat: PropTypes.string, estimatedMaximumFiat: PropTypes.string, - dappSuggestedGasFeeAcknowledged: PropTypes.boolean, + dappSuggestedGasFeeAcknowledged: PropTypes.bool, setDappSuggestedGasFeeAcknowledged: PropTypes.func, showAdvancedForm: PropTypes.bool, setShowAdvancedForm: PropTypes.func, diff --git a/ui/components/ui/radio-group/radio-group.component.js b/ui/components/ui/radio-group/radio-group.component.js index c59b300f0..e1c2f27e8 100644 --- a/ui/components/ui/radio-group/radio-group.component.js +++ b/ui/components/ui/radio-group/radio-group.component.js @@ -30,7 +30,6 @@ export default function RadioGroup({ options, name, selectedValue, onChange }) { onChange?.(option.value)} From 4552a53724e4e0cba423ec27e966b828f9584f23 Mon Sep 17 00:00:00 2001 From: Alex Donesky Date: Wed, 4 Aug 2021 08:24:40 -0500 Subject: [PATCH 16/55] remove unnecessary conversion call (#11742) * remove unnecessary conversion call * Pass args to GasTiming as decGwei * lint --- .../app/gas-timing/gas-timing.component.js | 7 +------ .../confirm-transaction-base.component.js | 13 +++++++++---- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/ui/components/app/gas-timing/gas-timing.component.js b/ui/components/app/gas-timing/gas-timing.component.js index 5b93c552a..3e2b4da7b 100644 --- a/ui/components/app/gas-timing/gas-timing.component.js +++ b/ui/components/app/gas-timing/gas-timing.component.js @@ -8,8 +8,6 @@ import { useGasFeeEstimates } from '../../../hooks/useGasFeeEstimates'; import { usePrevious } from '../../../hooks/usePrevious'; import { I18nContext } from '../../../contexts/i18n'; -import { hexWEIToDecGWEI } from '../../../helpers/utils/conversions.util'; - import Typography from '../../ui/typography/typography'; import { TYPOGRAPHY, @@ -63,10 +61,7 @@ export default function GasTiming({ (priority && priority !== previousMaxPriorityFeePerGas) || (fee && fee !== previousMaxFeePerGas) ) { - getGasFeeTimeEstimate( - hexWEIToDecGWEI(priority), - hexWEIToDecGWEI(fee), - ).then((result) => { + getGasFeeTimeEstimate(priority, fee).then((result) => { if (maxFeePerGas === fee && maxPriorityFeePerGas === priority) { setCustomEstimatedTime(result); } diff --git a/ui/pages/confirm-transaction-base/confirm-transaction-base.component.js b/ui/pages/confirm-transaction-base/confirm-transaction-base.component.js index 35a855e47..f162b1706 100644 --- a/ui/pages/confirm-transaction-base/confirm-transaction-base.component.js +++ b/ui/pages/confirm-transaction-base/confirm-transaction-base.component.js @@ -5,7 +5,11 @@ import { getEnvironmentType } from '../../../app/scripts/lib/util'; import ConfirmPageContainer from '../../components/app/confirm-page-container'; import { isBalanceSufficient } from '../send/send.utils'; import { getHexGasTotal } from '../../helpers/utils/confirm-tx.util'; -import { addHexes, hexToDecimal } from '../../helpers/utils/conversions.util'; +import { + addHexes, + hexToDecimal, + hexWEIToDecGWEI, +} from '../../helpers/utils/conversions.util'; import { CONFIRM_TRANSACTION_ROUTE, DEFAULT_ROUTE, @@ -19,7 +23,6 @@ import { } from '../../helpers/constants/error-keys'; import UserPreferencedCurrencyDisplay from '../../components/app/user-preferenced-currency-display'; import { PRIMARY, SECONDARY } from '../../helpers/constants/common'; - import TextField from '../../components/ui/text-field'; import { TRANSACTION_TYPES, @@ -397,8 +400,10 @@ export default class ConfirmTransactionBase extends Component { } subTitle={ } />, From 3f4c988018e7f3f44b7622c6b4ad87847538db90 Mon Sep 17 00:00:00 2001 From: Dan J Miller Date: Wed, 4 Aug 2021 18:27:14 -0230 Subject: [PATCH 17/55] Ensure gas fees update in popover on poll for new values (#11760) --- ui/hooks/useGasFeeInputs.js | 42 +++++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/ui/hooks/useGasFeeInputs.js b/ui/hooks/useGasFeeInputs.js index eb746c167..e2402ec10 100644 --- a/ui/hooks/useGasFeeInputs.js +++ b/ui/hooks/useGasFeeInputs.js @@ -206,41 +206,51 @@ export function useGasFeeInputs( estimatedGasFeeTimeBounds, } = useGasFeeEstimates(); + const [initialMaxFeePerGas] = useState( + Number(hexWEIToDecGWEI(transaction?.txParams?.maxFeePerGas)), + ); + const [initialMaxPriorityFeePerGas] = useState( + Number(hexWEIToDecGWEI(transaction?.txParams?.maxPriorityFeePerGas)), + ); + const [initialGasPrice] = useState( + Number(hexWEIToDecGWEI(transaction?.txParams?.gasPrice)), + ); + + const [initialMatchingEstimateLevel] = useState( + getMatchingEstimateFromGasFees( + gasFeeEstimates, + initialMaxFeePerGas, + initialMaxPriorityFeePerGas, + initialGasPrice, + networkAndAccountSupports1559, + ), + ); + // This hook keeps track of a few pieces of transitional state. It is // transitional because it is only used to modify a transaction in the // metamask (background) state tree. const [maxFeePerGas, setMaxFeePerGas] = useState( - transaction?.txParams?.maxFeePerGas - ? Number(hexWEIToDecGWEI(transaction.txParams.maxFeePerGas)) + initialMaxFeePerGas && !initialMatchingEstimateLevel + ? initialMaxFeePerGas : null, ); const [maxPriorityFeePerGas, setMaxPriorityFeePerGas] = useState( - transaction?.txParams?.maxPriorityFeePerGas - ? Number(hexWEIToDecGWEI(transaction.txParams.maxPriorityFeePerGas)) + initialMaxPriorityFeePerGas && !initialMatchingEstimateLevel + ? initialMaxPriorityFeePerGas : null, ); const [gasPriceHasBeenManuallySet, setGasPriceHasBeenManuallySet] = useState( false, ); const [gasPrice, setGasPrice] = useState( - transaction?.txParams?.gasPrice - ? Number(hexWEIToDecGWEI(transaction.txParams.gasPrice)) - : null, + initialGasPrice && !initialMatchingEstimateLevel ? initialGasPrice : null, ); const [gasLimit, setGasLimit] = useState( Number(hexToDecimal(transaction?.txParams?.gas ?? minimumGasLimit)), ); const [estimateToUse, setInternalEstimateToUse] = useState( - transaction - ? getMatchingEstimateFromGasFees( - gasFeeEstimates, - maxFeePerGas, - maxPriorityFeePerGas, - gasPrice, - networkAndAccountSupports1559, - ) - : defaultEstimateToUse, + transaction ? initialMatchingEstimateLevel : defaultEstimateToUse, ); // We specify whether to use the estimate value by checking if the state From a3c7bae3457d25e0e6ca3767643f21be078d61d1 Mon Sep 17 00:00:00 2001 From: David Walsh Date: Wed, 4 Aug 2021 16:00:46 -0500 Subject: [PATCH 18/55] EIP-1559 - Remove unncessary props from AdvancedFormControls (#11757) --- .../advanced-gas-controls.component.js | 46 +------------------ .../edit-gas-display.component.js | 4 -- .../edit-gas-popover.component.js | 2 - 3 files changed, 1 insertion(+), 51 deletions(-) diff --git a/ui/components/app/advanced-gas-controls/advanced-gas-controls.component.js b/ui/components/app/advanced-gas-controls/advanced-gas-controls.component.js index e46560131..66ced39b1 100644 --- a/ui/components/app/advanced-gas-controls/advanced-gas-controls.component.js +++ b/ui/components/app/advanced-gas-controls/advanced-gas-controls.component.js @@ -4,16 +4,11 @@ import { useSelector } from 'react-redux'; import { I18nContext } from '../../../contexts/i18n'; import FormField from '../../ui/form-field'; -import { - GAS_ESTIMATE_TYPES, - GAS_RECOMMENDATIONS, -} from '../../../../shared/constants/gas'; +import { GAS_ESTIMATE_TYPES } from '../../../../shared/constants/gas'; import { getGasFormErrorText } from '../../../helpers/constants/gas'; import { checkNetworkAndAccountSupports1559 } from '../../../selectors'; export default function AdvancedGasControls({ - estimateToUse, - gasFeeEstimates, gasEstimateType, maxPriorityFee, maxFee, @@ -34,28 +29,6 @@ export default function AdvancedGasControls({ checkNetworkAndAccountSupports1559, ); - const suggestedValues = {}; - - if (networkAndAccountSupport1559) { - suggestedValues.maxFeePerGas = - gasFeeEstimates?.[estimateToUse]?.suggestedMaxFeePerGas || - gasFeeEstimates?.gasPrice; - suggestedValues.maxPriorityFeePerGas = - gasFeeEstimates?.[estimateToUse]?.suggestedMaxPriorityFeePerGas || - suggestedValues.maxFeePerGas; - } else { - switch (gasEstimateType) { - case GAS_ESTIMATE_TYPES.LEGACY: - suggestedValues.gasPrice = gasFeeEstimates?.[estimateToUse]; - break; - case GAS_ESTIMATE_TYPES.ETH_GASPRICE: - suggestedValues.gasPrice = gasFeeEstimates?.gasPrice; - break; - default: - break; - } - } - const showFeeMarketFields = networkAndAccountSupport1559 && (gasEstimateType === GAS_ESTIMATE_TYPES.FEE_MARKET || @@ -142,23 +115,6 @@ export default function AdvancedGasControls({ } AdvancedGasControls.propTypes = { - estimateToUse: PropTypes.oneOf(Object.values(GAS_RECOMMENDATIONS)), - gasFeeEstimates: PropTypes.oneOf([ - PropTypes.shape({ - gasPrice: PropTypes.string, - }), - PropTypes.shape({ - low: PropTypes.string, - medium: PropTypes.string, - high: PropTypes.string, - }), - PropTypes.shape({ - low: PropTypes.object, - medium: PropTypes.object, - high: PropTypes.object, - estimatedBaseFee: PropTypes.string, - }), - ]), gasEstimateType: PropTypes.oneOf(Object.values(GAS_ESTIMATE_TYPES)), setMaxPriorityFee: PropTypes.func, setMaxFee: PropTypes.func, diff --git a/ui/components/app/edit-gas-display/edit-gas-display.component.js b/ui/components/app/edit-gas-display/edit-gas-display.component.js index 306987118..aba45f521 100644 --- a/ui/components/app/edit-gas-display/edit-gas-display.component.js +++ b/ui/components/app/edit-gas-display/edit-gas-display.component.js @@ -47,7 +47,6 @@ export default function EditGasDisplay({ estimatedMaximumNative, estimatedMinimumNative, isGasEstimatesLoading, - gasFeeEstimates, gasEstimateType, gasPrice, setGasPrice, @@ -216,9 +215,7 @@ export default function EditGasDisplay({ )} {!requireDappAcknowledgement && showAdvancedForm && ( Date: Wed, 4 Aug 2021 16:16:18 -0500 Subject: [PATCH 19/55] EIP-1559 - Temporarily remove the loading heartbeat from the transaction detail (#11762) --- .../app/transaction-detail/transaction-detail.component.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/ui/components/app/transaction-detail/transaction-detail.component.js b/ui/components/app/transaction-detail/transaction-detail.component.js index 4e72f0ca0..0829df684 100644 --- a/ui/components/app/transaction-detail/transaction-detail.component.js +++ b/ui/components/app/transaction-detail/transaction-detail.component.js @@ -2,20 +2,14 @@ import React, { useContext } from 'react'; import PropTypes from 'prop-types'; import { I18nContext } from '../../../contexts/i18n'; -import { useShouldAnimateGasEstimations } from '../../../hooks/useShouldAnimateGasEstimations'; import TransactionDetailItem from '../transaction-detail-item/transaction-detail-item.component'; -import LoadingHeartBeat from '../../ui/loading-heartbeat'; export default function TransactionDetail({ rows = [], onEdit }) { const t = useContext(I18nContext); - const shouldAnimate = useShouldAnimateGasEstimations(); return (
- {process.env.IN_TEST === 'true' ? null : ( - - )} {onEdit && (
From aa3d1f2341a1330b47ac592944839af62d054d99 Mon Sep 17 00:00:00 2001 From: David Walsh Date: Wed, 4 Aug 2021 16:17:20 -0500 Subject: [PATCH 20/55] EIP-1559 - Prevent uncaught exception when passing fees to getGasFeeTimeEstimate (#11759) --- ui/components/app/gas-timing/gas-timing.component.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/ui/components/app/gas-timing/gas-timing.component.js b/ui/components/app/gas-timing/gas-timing.component.js index 3e2b4da7b..7c94732e5 100644 --- a/ui/components/app/gas-timing/gas-timing.component.js +++ b/ui/components/app/gas-timing/gas-timing.component.js @@ -1,6 +1,7 @@ import React, { useContext, useEffect, useState } from 'react'; import PropTypes from 'prop-types'; import classNames from 'classnames'; +import BigNumber from 'bignumber.js'; import { GAS_ESTIMATE_TYPES } from '../../../../shared/constants/gas'; @@ -61,7 +62,11 @@ export default function GasTiming({ (priority && priority !== previousMaxPriorityFeePerGas) || (fee && fee !== previousMaxFeePerGas) ) { - getGasFeeTimeEstimate(priority, fee).then((result) => { + // getGasFeeTimeEstimate requires parameters in string format + getGasFeeTimeEstimate( + new BigNumber(priority).toString(10), + new BigNumber(fee).toString(10), + ).then((result) => { if (maxFeePerGas === fee && maxPriorityFeePerGas === priority) { setCustomEstimatedTime(result); } From a950111a19aabc94cc5348dbf25249098b7b015b Mon Sep 17 00:00:00 2001 From: Dan J Miller Date: Wed, 4 Aug 2021 19:04:42 -0230 Subject: [PATCH 21/55] Show advanced options, and hide radio buttons, for advanced gas settings users (#11751) * Show advanced options, and hide radio buttons, for users with advanced gas settings turned on * Improve naming and copy * lint fix * Lint fix --- app/_locales/en/messages.json | 3 + .../edit-gas-display.component.js | 105 +++++++++++------- .../edit-gas-popover.component.js | 7 +- ui/hooks/useGasFeeInputs.js | 13 ++- 4 files changed, 82 insertions(+), 46 deletions(-) diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index 5f4efe357..bf51a59ce 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -1935,6 +1935,9 @@ "showPrivateKeys": { "message": "Show Private Keys" }, + "showRecommendations": { + "message": "Show Recommendations" + }, "showSeedPhrase": { "message": "Show Secret Recovery Phrase" }, diff --git a/ui/components/app/edit-gas-display/edit-gas-display.component.js b/ui/components/app/edit-gas-display/edit-gas-display.component.js index aba45f521..22216a2a7 100644 --- a/ui/components/app/edit-gas-display/edit-gas-display.component.js +++ b/ui/components/app/edit-gas-display/edit-gas-display.component.js @@ -1,4 +1,4 @@ -import React, { useContext } from 'react'; +import React, { useContext, useState } from 'react'; import { useSelector } from 'react-redux'; import PropTypes from 'prop-types'; @@ -13,6 +13,7 @@ import Typography from '../../ui/typography/typography'; import { getIsMainnet, checkNetworkAndAccountSupports1559, + getAdvancedInlineGasShown, } from '../../../selectors'; import { @@ -58,20 +59,29 @@ export default function EditGasDisplay({ estimatedMaximumFiat, dappSuggestedGasFeeAcknowledged, setDappSuggestedGasFeeAcknowledged, - showAdvancedForm, - setShowAdvancedForm, warning, gasErrors, onManualChange, minimumGasLimit, balanceError, estimatesUnavailableWarning, + hasGasErrors, }) { const t = useContext(I18nContext); const isMainnet = useSelector(getIsMainnet); const networkAndAccountSupport1559 = useSelector( checkNetworkAndAccountSupports1559, ); + const showAdvancedInlineGasIfPossible = useSelector( + getAdvancedInlineGasShown, + ); + + const [showAdvancedForm, setShowAdvancedForm] = useState( + !estimateToUse || hasGasErrors || !networkAndAccountSupport1559, + ); + const [hideRadioButtons, setHideRadioButtons] = useState( + showAdvancedInlineGasIfPossible, + ); const dappSuggestedAndTxParamGasFeesAreTheSame = areDappSuggestedAndTxParamGasFeesTheSame( transaction, @@ -84,7 +94,7 @@ export default function EditGasDisplay({ ); const showTopError = balanceError || estimatesUnavailableWarning; - const showRadioButtons = + const radioButtonsEnabled = networkAndAccountSupport1559 && gasEstimateType === GAS_ESTIMATE_TYPES.FEE_MARKET && !requireDappAcknowledgement && @@ -175,7 +185,22 @@ export default function EditGasDisplay({ {t('gasDisplayAcknowledgeDappButtonText')} )} - {showRadioButtons && ( + {!requireDappAcknowledgement && + radioButtonsEnabled && + showAdvancedInlineGasIfPossible && ( + + )} + {radioButtonsEnabled && !hideRadioButtons && ( )} - {!requireDappAcknowledgement && showRadioButtons && ( - - )} - {!requireDappAcknowledgement && showAdvancedForm && ( - - )} + {!requireDappAcknowledgement && + radioButtonsEnabled && + !showAdvancedInlineGasIfPossible && ( + + )} + {!requireDappAcknowledgement && + (showAdvancedForm || showAdvancedInlineGasIfPossible) && ( + + )}
{networkAndAccountSupport1559 && !requireDappAcknowledgement && @@ -271,8 +299,6 @@ EditGasDisplay.propTypes = { estimatedMaximumFiat: PropTypes.string, dappSuggestedGasFeeAcknowledged: PropTypes.bool, setDappSuggestedGasFeeAcknowledged: PropTypes.func, - showAdvancedForm: PropTypes.bool, - setShowAdvancedForm: PropTypes.func, warning: PropTypes.string, transaction: PropTypes.object, gasErrors: PropTypes.object, @@ -280,4 +306,5 @@ EditGasDisplay.propTypes = { minimumGasLimit: PropTypes.number, balanceError: PropTypes.bool, estimatesUnavailableWarning: PropTypes.bool, + hasGasErrors: PropTypes.bool, }; diff --git a/ui/components/app/edit-gas-popover/edit-gas-popover.component.js b/ui/components/app/edit-gas-popover/edit-gas-popover.component.js index 68101a2ac..e77554d17 100644 --- a/ui/components/app/edit-gas-popover/edit-gas-popover.component.js +++ b/ui/components/app/edit-gas-popover/edit-gas-popover.component.js @@ -87,10 +87,6 @@ export default function EditGasPopover({ estimatesUnavailableWarning, } = useGasFeeInputs(defaultEstimateToUse, transaction, minimumGasLimit, mode); - const [showAdvancedForm, setShowAdvancedForm] = useState( - !estimateToUse || hasGasErrors || !networkAndAccountSupport1559, - ); - /** * Temporary placeholder, this should be managed by the parent component but * we will be extracting this component from the hard to maintain modal/ @@ -213,8 +209,6 @@ export default function EditGasPopover({ diff --git a/ui/hooks/useGasFeeInputs.js b/ui/hooks/useGasFeeInputs.js index e2402ec10..d84af73b9 100644 --- a/ui/hooks/useGasFeeInputs.js +++ b/ui/hooks/useGasFeeInputs.js @@ -21,6 +21,7 @@ import { checkNetworkAndAccountSupports1559, getShouldShowFiat, getSelectedAccount, + getAdvancedInlineGasShown, } from '../selectors'; import { @@ -249,8 +250,18 @@ export function useGasFeeInputs( Number(hexToDecimal(transaction?.txParams?.gas ?? minimumGasLimit)), ); + const userPrefersAdvancedGas = useSelector(getAdvancedInlineGasShown); + const dontDefaultToAnEstimateLevel = + userPrefersAdvancedGas && + transaction?.txParams?.maxPriorityFeePerGas && + transaction?.txParams?.gasPrice; + + const initialEstimateToUse = transaction + ? initialMatchingEstimateLevel + : defaultEstimateToUse; + const [estimateToUse, setInternalEstimateToUse] = useState( - transaction ? initialMatchingEstimateLevel : defaultEstimateToUse, + dontDefaultToAnEstimateLevel ? null : initialEstimateToUse, ); // We specify whether to use the estimate value by checking if the state From 96a13dddb537c344626482a83e3aa635c2b6b32a Mon Sep 17 00:00:00 2001 From: David Walsh Date: Wed, 4 Aug 2021 16:37:16 -0500 Subject: [PATCH 22/55] Provide pointer cursor for radio group buttons (#11763) --- ui/components/ui/radio-group/index.scss | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ui/components/ui/radio-group/index.scss b/ui/components/ui/radio-group/index.scss index 7aab3307d..3c9685920 100644 --- a/ui/components/ui/radio-group/index.scss +++ b/ui/components/ui/radio-group/index.scss @@ -40,6 +40,10 @@ &__column-radio { margin-inline-end: 1px; + + input { + cursor: pointer; + } } &__column-radio, From d359429f0473c3e73a6d14bf8d7b37cb301865cc Mon Sep 17 00:00:00 2001 From: Alex Donesky Date: Wed, 4 Aug 2021 16:53:13 -0500 Subject: [PATCH 23/55] Stop GasFeeController polling when pop closes (#11746) * Stop GasFeeController polling when pop closes * Stop estimate gas polling on window unload * lint + comments * Improve client closed logic * lint * Add back _beforeUnload on unmount in gas-modal-page-container * Add full check and call onClientClosed method for notifcation environment * Add gas pollingToken tracking to appStateController and use to disconnect polling for each environment type * remove unused method * move controller manipulation logic from background.js to metamask-controller, disaggregate methods * add beforeunload handling to reset gas polling tokens from root of send page * cleanup, lint and address feedback * clear appState gasPollingTokens when all instances of all env types are closed, fix pollingTokenType arg from onEnvironmentTypeClosed call in metamask-controller * mock new methods to fix tests * final bit of cleanup + comments Co-authored-by: Dan Miller --- app/scripts/background.js | 37 ++++++++++++++-- app/scripts/controllers/app-state.js | 37 ++++++++++++++++ app/scripts/metamask-controller.js | 44 ++++++++++++++++++- shared/constants/app.js | 6 +++ ...gas-modal-page-container-component.test.js | 1 + .../gas-modal-page-container.component.js | 13 +++++- ui/ducks/send/send.js | 7 ++- ui/hooks/useGasFeeEstimates.test.js | 2 + ui/hooks/useSafeGasEstimatePolling.js | 30 +++++++++---- .../confirm-transaction-base.component.js | 19 ++++++-- .../confirm-transaction.component.js | 20 +++++++-- ui/pages/send/send.js | 13 ++++-- ui/store/actions.js | 19 +++++++- 13 files changed, 221 insertions(+), 27 deletions(-) diff --git a/app/scripts/background.js b/app/scripts/background.js index bf7dd24cc..56044213c 100644 --- a/app/scripts/background.js +++ b/app/scripts/background.js @@ -302,6 +302,24 @@ function setupController(initState, initLangCode) { ); }; + const onCloseEnvironmentInstances = (isClientOpen, environmentType) => { + // if all instances of metamask are closed we call a method on the controller to stop gasFeeController polling + if (isClientOpen === false) { + controller.onClientClosed(); + // otherwise we want to only remove the polling tokens for the environment type that has closed + } else { + // in the case of fullscreen environment a user might have multiple tabs open so we don't want to disconnect all of + // its corresponding polling tokens unless all tabs are closed. + if ( + environmentType === ENVIRONMENT_TYPE_FULLSCREEN && + Boolean(Object.keys(openMetamaskTabsIDs).length) + ) { + return; + } + controller.onEnvironmentTypeClosed(environmentType); + } + }; + /** * A runtime.Port object, as provided by the browser: * @see https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/runtime/Port @@ -330,10 +348,11 @@ function setupController(initState, initLangCode) { if (processName === ENVIRONMENT_TYPE_POPUP) { popupIsOpen = true; - endOfStream(portStream, () => { popupIsOpen = false; - controller.isClientOpen = isClientOpenStatus(); + const isClientOpen = isClientOpenStatus(); + controller.isClientOpen = isClientOpen; + onCloseEnvironmentInstances(isClientOpen, ENVIRONMENT_TYPE_POPUP); }); } @@ -342,7 +361,12 @@ function setupController(initState, initLangCode) { endOfStream(portStream, () => { notificationIsOpen = false; - controller.isClientOpen = isClientOpenStatus(); + const isClientOpen = isClientOpenStatus(); + controller.isClientOpen = isClientOpen; + onCloseEnvironmentInstances( + isClientOpen, + ENVIRONMENT_TYPE_NOTIFICATION, + ); }); } @@ -352,7 +376,12 @@ function setupController(initState, initLangCode) { endOfStream(portStream, () => { delete openMetamaskTabsIDs[tabId]; - controller.isClientOpen = isClientOpenStatus(); + const isClientOpen = isClientOpenStatus(); + controller.isClientOpen = isClientOpen; + onCloseEnvironmentInstances( + isClientOpen, + ENVIRONMENT_TYPE_FULLSCREEN, + ); }); } } else { diff --git a/app/scripts/controllers/app-state.js b/app/scripts/controllers/app-state.js index 9916e5d4f..84ee14394 100644 --- a/app/scripts/controllers/app-state.js +++ b/app/scripts/controllers/app-state.js @@ -25,6 +25,9 @@ export default class AppStateController extends EventEmitter { connectedStatusPopoverHasBeenShown: true, defaultHomeActiveTabName: null, browserEnvironment: {}, + popupGasPollTokens: [], + notificationGasPollTokens: [], + fullScreenGasPollTokens: [], recoveryPhraseReminderHasBeenShown: false, recoveryPhraseReminderLastShown: new Date().getTime(), ...initState, @@ -191,4 +194,38 @@ export default class AppStateController extends EventEmitter { setBrowserEnvironment(os, browser) { this.store.updateState({ browserEnvironment: { os, browser } }); } + + /** + * Adds a pollingToken for a given environmentType + * @returns {void} + */ + addPollingToken(pollingToken, pollingTokenType) { + const prevState = this.store.getState()[pollingTokenType]; + this.store.updateState({ + [pollingTokenType]: [...prevState, pollingToken], + }); + } + + /** + * removes a pollingToken for a given environmentType + * @returns {void} + */ + removePollingToken(pollingToken, pollingTokenType) { + const prevState = this.store.getState()[pollingTokenType]; + this.store.updateState({ + [pollingTokenType]: prevState.filter((token) => token !== pollingToken), + }); + } + + /** + * clears all pollingTokens + * @returns {void} + */ + clearPollingTokens() { + this.store.updateState({ + popupGasPollTokens: [], + notificationGasPollTokens: [], + fullScreenGasPollTokens: [], + }); + } } diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 99eff2ab3..f1c2cba85 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -32,6 +32,7 @@ import { MAINNET_CHAIN_ID } from '../../shared/constants/network'; import { UI_NOTIFICATIONS } from '../../shared/notifications'; import { toChecksumHexAddress } from '../../shared/modules/hexstring-utils'; import { MILLISECOND } from '../../shared/constants/time'; +import { POLLING_TOKEN_ENVIRONMENT_TYPES } from '../../shared/constants/app'; import { hexToDecimal } from '../../ui/helpers/utils/conversions.util'; import ComposableObservableStore from './lib/ComposableObservableStore'; @@ -1130,6 +1131,16 @@ export default class MetamaskController extends EventEmitter { this.gasFeeController.getTimeEstimate, this.gasFeeController, ), + + addPollingTokenToAppState: nodeify( + this.appStateController.addPollingToken, + this.appStateController, + ), + + removePollingTokenFromAppState: nodeify( + this.appStateController.removePollingToken, + this.appStateController, + ), }; } @@ -2969,7 +2980,6 @@ export default class MetamaskController extends EventEmitter { /* eslint-disable accessor-pairs */ /** * A method for recording whether the MetaMask user interface is open or not. - * @private * @param {boolean} open */ set isClientOpen(open) { @@ -2978,6 +2988,38 @@ export default class MetamaskController extends EventEmitter { } /* eslint-enable accessor-pairs */ + /** + * A method that is called by the background when all instances of metamask are closed. + * Currently used to stop polling in the gasFeeController. + */ + onClientClosed() { + try { + this.gasFeeController.stopPolling(); + this.appStateController.clearPollingTokens(); + } catch (error) { + console.error(error); + } + } + + /** + * A method that is called by the background when a particular environment type is closed (fullscreen, popup, notification). + * Currently used to stop polling in the gasFeeController for only that environement type + */ + onEnvironmentTypeClosed(environmentType) { + const appStatePollingTokenType = + POLLING_TOKEN_ENVIRONMENT_TYPES[environmentType]; + const pollingTokensToDisconnect = this.appStateController.store.getState()[ + appStatePollingTokenType + ]; + pollingTokensToDisconnect.forEach((pollingToken) => { + this.gasFeeController.disconnectPoller(pollingToken); + this.appStateController.removePollingToken( + pollingToken, + appStatePollingTokenType, + ); + }); + } + /** * Adds a domain to the PhishingController safelist * @param {string} hostname - the domain to safelist diff --git a/shared/constants/app.js b/shared/constants/app.js index 7e1b66572..a278a14bd 100644 --- a/shared/constants/app.js +++ b/shared/constants/app.js @@ -30,3 +30,9 @@ export const MESSAGE_TYPE = { ADD_ETHEREUM_CHAIN: 'wallet_addEthereumChain', SWITCH_ETHEREUM_CHAIN: 'wallet_switchEthereumChain', }; + +export const POLLING_TOKEN_ENVIRONMENT_TYPES = { + [ENVIRONMENT_TYPE_POPUP]: 'popupGasPollTokens', + [ENVIRONMENT_TYPE_NOTIFICATION]: 'notificationGasPollTokens', + [ENVIRONMENT_TYPE_FULLSCREEN]: 'fullScreenGasPollTokens', +}; diff --git a/ui/components/app/gas-customization/gas-modal-page-container/gas-modal-page-container-component.test.js b/ui/components/app/gas-customization/gas-modal-page-container/gas-modal-page-container-component.test.js index 5b8f5c665..185f1b3a7 100644 --- a/ui/components/app/gas-customization/gas-modal-page-container/gas-modal-page-container-component.test.js +++ b/ui/components/app/gas-customization/gas-modal-page-container/gas-modal-page-container-component.test.js @@ -13,6 +13,7 @@ jest.mock('../../../../store/actions', () => ({ getGasFeeEstimatesAndStartPolling: jest .fn() .mockImplementation(() => Promise.resolve()), + addPollingTokenToAppState: jest.fn(), })); const propsMethodSpies = { diff --git a/ui/components/app/gas-customization/gas-modal-page-container/gas-modal-page-container.component.js b/ui/components/app/gas-customization/gas-modal-page-container/gas-modal-page-container.component.js index aaa28a056..9bf9ccfa9 100644 --- a/ui/components/app/gas-customization/gas-modal-page-container/gas-modal-page-container.component.js +++ b/ui/components/app/gas-customization/gas-modal-page-container/gas-modal-page-container.component.js @@ -5,6 +5,8 @@ import { Tabs, Tab } from '../../../ui/tabs'; import { disconnectGasFeeEstimatePoller, getGasFeeEstimatesAndStartPolling, + addPollingTokenToAppState, + removePollingTokenFromAppState, } from '../../../../store/actions'; import AdvancedTabContent from './advanced-tab-content'; import BasicTabContent from './basic-tab-content'; @@ -52,18 +54,27 @@ export default class GasModalPageContainer extends Component { this._isMounted = true; getGasFeeEstimatesAndStartPolling().then((pollingToken) => { if (this._isMounted) { + addPollingTokenToAppState(pollingToken); this.setState({ pollingToken }); } else { disconnectGasFeeEstimatePoller(pollingToken); + removePollingTokenFromAppState(pollingToken); } }); + window.addEventListener('beforeunload', this._beforeUnload); } - componentWillUnmount() { + _beforeUnload = () => { this._isMounted = false; if (this.state.pollingToken) { disconnectGasFeeEstimatePoller(this.state.pollingToken); + removePollingTokenFromAppState(this.state.pollingToken); } + }; + + componentWillUnmount() { + this._beforeUnload(); + window.removeEventListener('beforeunload', this._beforeUnload); } renderBasicTabContent(gasPriceButtonGroupProps) { diff --git a/ui/ducks/send/send.js b/ui/ducks/send/send.js index 5c9badd91..2d58b7137 100644 --- a/ui/ducks/send/send.js +++ b/ui/ducks/send/send.js @@ -50,6 +50,8 @@ import { showLoadingIndication, updateTokenType, updateTransaction, + addPollingTokenToAppState, + removePollingTokenFromAppState, } from '../../store/actions'; import { setCustomGasLimit } from '../gas/gas.duck'; import { @@ -84,7 +86,6 @@ import { import { CHAIN_ID_TO_GAS_LIMIT_BUFFER_MAP } from '../../../shared/constants/network'; import { ETH, GWEI } from '../../helpers/constants/common'; import { TRANSACTION_ENVELOPE_TYPES } from '../../../shared/constants/transaction'; - // typedefs /** * @typedef {import('@reduxjs/toolkit').PayloadAction} PayloadAction @@ -437,6 +438,9 @@ export const initializeSendState = createAsyncThunk( // Instruct the background process that polling for gas prices should begin gasEstimatePollToken = await getGasFeeEstimatesAndStartPolling(); + + addPollingTokenToAppState(gasEstimatePollToken); + const { metamask: { gasFeeEstimates, gasEstimateType }, } = thunkApi.getState(); @@ -1298,6 +1302,7 @@ export function resetSendState() { await disconnectGasFeeEstimatePoller( state[name].gas.gasEstimatePollToken, ); + removePollingTokenFromAppState(state[name].gas.gasEstimatePollToken); } }; } diff --git a/ui/hooks/useGasFeeEstimates.test.js b/ui/hooks/useGasFeeEstimates.test.js index 030806667..0af584e1c 100644 --- a/ui/hooks/useGasFeeEstimates.test.js +++ b/ui/hooks/useGasFeeEstimates.test.js @@ -16,6 +16,8 @@ import { useGasFeeEstimates } from './useGasFeeEstimates'; jest.mock('../store/actions', () => ({ disconnectGasFeeEstimatePoller: jest.fn(), getGasFeeEstimatesAndStartPolling: jest.fn(), + addPollingTokenToAppState: jest.fn(), + removePollingTokenFromAppState: jest.fn(), })); jest.mock('react-redux', () => { diff --git a/ui/hooks/useSafeGasEstimatePolling.js b/ui/hooks/useSafeGasEstimatePolling.js index a5b803eb7..156f805e6 100644 --- a/ui/hooks/useSafeGasEstimatePolling.js +++ b/ui/hooks/useSafeGasEstimatePolling.js @@ -2,6 +2,8 @@ import { useEffect } from 'react'; import { disconnectGasFeeEstimatePoller, getGasFeeEstimatesAndStartPolling, + addPollingTokenToAppState, + removePollingTokenFromAppState, } from '../store/actions'; /** @@ -16,18 +18,30 @@ export function useSafeGasEstimatePolling() { useEffect(() => { let active = true; let pollToken; - getGasFeeEstimatesAndStartPolling().then((newPollToken) => { - if (active) { - pollToken = newPollToken; - } else { - disconnectGasFeeEstimatePoller(newPollToken); - } - }); - return () => { + + const cleanup = () => { active = false; if (pollToken) { disconnectGasFeeEstimatePoller(pollToken); + removePollingTokenFromAppState(pollToken); } }; + + getGasFeeEstimatesAndStartPolling().then((newPollToken) => { + if (active) { + pollToken = newPollToken; + addPollingTokenToAppState(pollToken); + } else { + disconnectGasFeeEstimatePoller(newPollToken); + removePollingTokenFromAppState(pollToken); + } + }); + + window.addEventListener('beforeunload', cleanup); + + return () => { + cleanup(); + window.removeEventListener('beforeunload', cleanup); + }; }, []); } diff --git a/ui/pages/confirm-transaction-base/confirm-transaction-base.component.js b/ui/pages/confirm-transaction-base/confirm-transaction-base.component.js index f162b1706..164573169 100644 --- a/ui/pages/confirm-transaction-base/confirm-transaction-base.component.js +++ b/ui/pages/confirm-transaction-base/confirm-transaction-base.component.js @@ -40,6 +40,8 @@ import { COLORS } from '../../helpers/constants/design-system'; import { disconnectGasFeeEstimatePoller, getGasFeeEstimatesAndStartPolling, + addPollingTokenToAppState, + removePollingTokenFromAppState, } from '../../store/actions'; export default class ConfirmTransactionBase extends Component { @@ -679,10 +681,19 @@ export default class ConfirmTransactionBase extends Component { cancelTransaction({ id }); }; + _beforeUnloadForGasPolling = () => { + this._isMounted = false; + if (this.state.pollingToken) { + disconnectGasFeeEstimatePoller(this.state.pollingToken); + removePollingTokenFromAppState(this.state.pollingToken); + } + }; + _removeBeforeUnload = () => { if (getEnvironmentType() === ENVIRONMENT_TYPE_NOTIFICATION) { window.removeEventListener('beforeunload', this._beforeUnload); } + window.removeEventListener('beforeunload', this._beforeUnloadForGasPolling); }; componentDidMount() { @@ -723,18 +734,18 @@ export default class ConfirmTransactionBase extends Component { */ getGasFeeEstimatesAndStartPolling().then((pollingToken) => { if (this._isMounted) { + addPollingTokenToAppState(pollingToken); this.setState({ pollingToken }); } else { disconnectGasFeeEstimatePoller(pollingToken); + removePollingTokenFromAppState(this.state.pollingToken); } }); + window.addEventListener('beforeunload', this._beforeUnloadForGasPolling); } componentWillUnmount() { - this._isMounted = false; - if (this.state.pollingToken) { - disconnectGasFeeEstimatePoller(this.state.pollingToken); - } + this._beforeUnloadForGasPolling(); this._removeBeforeUnload(); } diff --git a/ui/pages/confirm-transaction/confirm-transaction.component.js b/ui/pages/confirm-transaction/confirm-transaction.component.js index 2988739e6..d28904544 100644 --- a/ui/pages/confirm-transaction/confirm-transaction.component.js +++ b/ui/pages/confirm-transaction/confirm-transaction.component.js @@ -28,6 +28,8 @@ import { import { disconnectGasFeeEstimatePoller, getGasFeeEstimatesAndStartPolling, + addPollingTokenToAppState, + removePollingTokenFromAppState, } from '../../store/actions'; import ConfTx from './conf-tx'; @@ -57,6 +59,14 @@ export default class ConfirmTransaction extends Component { this.state = {}; } + _beforeUnload = () => { + this._isMounted = false; + if (this.state.pollingToken) { + disconnectGasFeeEstimatePoller(this.state.pollingToken); + removePollingTokenFromAppState(this.state.pollingToken); + } + }; + componentDidMount() { this._isMounted = true; const { @@ -75,11 +85,15 @@ export default class ConfirmTransaction extends Component { getGasFeeEstimatesAndStartPolling().then((pollingToken) => { if (this._isMounted) { this.setState({ pollingToken }); + addPollingTokenToAppState(pollingToken); } else { disconnectGasFeeEstimatePoller(pollingToken); + removePollingTokenFromAppState(pollingToken); } }); + window.addEventListener('beforeunload', this._beforeUnload); + if (!totalUnapprovedCount && !sendTo) { history.replace(mostRecentOverviewPage); return; @@ -96,10 +110,8 @@ export default class ConfirmTransaction extends Component { } componentWillUnmount() { - this._isMounted = false; - if (this.state.pollingToken) { - disconnectGasFeeEstimatePoller(this.state.pollingToken); - } + this._beforeUnload(); + window.removeEventListener('beforeunload', this._beforeUnload); } componentDidUpdate(prevProps) { diff --git a/ui/pages/send/send.js b/ui/pages/send/send.js index 1e908d9de..3c5515867 100644 --- a/ui/pages/send/send.js +++ b/ui/pages/send/send.js @@ -1,4 +1,4 @@ -import React, { useEffect } from 'react'; +import React, { useEffect, useCallback } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { useHistory, useLocation } from 'react-router-dom'; import { @@ -47,11 +47,17 @@ export default function SendTransactionScreen() { }); const dispatch = useDispatch(); + + const cleanup = useCallback(() => { + dispatch(resetSendState()); + }, [dispatch]); + useEffect(() => { if (chainId !== undefined) { dispatch(initializeSendState()); + window.addEventListener('beforeunload', cleanup); } - }, [chainId, dispatch]); + }, [chainId, dispatch, cleanup]); useEffect(() => { if (location.search === '?scan=true') { @@ -67,8 +73,9 @@ export default function SendTransactionScreen() { useEffect(() => { return () => { dispatch(resetSendState()); + window.removeEventListener('beforeunload', cleanup); }; - }, [dispatch]); + }, [dispatch, cleanup]); let content; diff --git a/ui/store/actions.js b/ui/store/actions.js index 64fe017fb..05001e99e 100644 --- a/ui/store/actions.js +++ b/ui/store/actions.js @@ -10,7 +10,10 @@ import { import { getMethodDataAsync } from '../helpers/utils/transactions.util'; import { getSymbolAndDecimals } from '../helpers/utils/token-util'; import switchDirection from '../helpers/utils/switch-direction'; -import { ENVIRONMENT_TYPE_NOTIFICATION } from '../../shared/constants/app'; +import { + ENVIRONMENT_TYPE_NOTIFICATION, + POLLING_TOKEN_ENVIRONMENT_TYPES, +} from '../../shared/constants/app'; import { hasUnconfirmedTransactions } from '../helpers/utils/confirm-tx.util'; import txHelper from '../helpers/utils/tx-helper'; import { getEnvironmentType, addHexPrefix } from '../../app/scripts/lib/util'; @@ -2787,6 +2790,20 @@ export function disconnectGasFeeEstimatePoller(pollToken) { return promisifiedBackground.disconnectGasFeeEstimatePoller(pollToken); } +export async function addPollingTokenToAppState(pollingToken) { + return promisifiedBackground.addPollingTokenToAppState( + pollingToken, + POLLING_TOKEN_ENVIRONMENT_TYPES[getEnvironmentType()], + ); +} + +export async function removePollingTokenFromAppState(pollingToken) { + return promisifiedBackground.removePollingTokenFromAppState( + pollingToken, + POLLING_TOKEN_ENVIRONMENT_TYPES[getEnvironmentType()], + ); +} + export function getGasFeeTimeEstimate(maxPriorityFeePerGas, maxFeePerGas) { return promisifiedBackground.getGasFeeTimeEstimate( maxPriorityFeePerGas, From e1de6e19af91404bbce9b0a798f860f6299f489c Mon Sep 17 00:00:00 2001 From: David Walsh Date: Wed, 4 Aug 2021 17:07:17 -0500 Subject: [PATCH 24/55] EIP-1559 - Fix mislabeled MaxPriority fee key (#11758) --- ui/helpers/constants/gas.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/helpers/constants/gas.js b/ui/helpers/constants/gas.js index ae232c8a7..4f2d8612b 100644 --- a/ui/helpers/constants/gas.js +++ b/ui/helpers/constants/gas.js @@ -2,7 +2,7 @@ export const GAS_FORM_ERRORS = { GAS_LIMIT_OUT_OF_BOUNDS: 'editGasLimitOutOfBounds', MAX_PRIORITY_FEE_TOO_LOW: 'editGasMaxPriorityFeeLow', MAX_FEE_TOO_LOW: 'editGasMaxFeeLow', - MAX_PRIORITY_FEE_ZERO: 'editGasMaxPriorityFeeZeroError', + MAX_PRIORITY_FEE_BELOW_MINIMUM: 'editGasMaxPriorityFeeBelowMinimum', MAX_PRIORITY_FEE_HIGH_WARNING: 'editGasMaxPriorityFeeHigh', MAX_FEE_HIGH_WARNING: 'editGasMaxFeeHigh', MAX_FEE_IMBALANCE: 'editGasMaxFeeImbalance', From 4245dac8d930ac25b9c7415347e9e5b064d1282b Mon Sep 17 00:00:00 2001 From: Alex Donesky Date: Wed, 4 Aug 2021 17:10:29 -0500 Subject: [PATCH 25/55] tiny lost code change from 9.8.4 RC (#11764) --- .../app/transaction-breakdown/transaction-breakdown.component.js | 1 + 1 file changed, 1 insertion(+) diff --git a/ui/components/app/transaction-breakdown/transaction-breakdown.component.js b/ui/components/app/transaction-breakdown/transaction-breakdown.component.js index 6f1f5ab9c..f0a314c46 100644 --- a/ui/components/app/transaction-breakdown/transaction-breakdown.component.js +++ b/ui/components/app/transaction-breakdown/transaction-breakdown.component.js @@ -143,6 +143,7 @@ export default class TransactionBreakdown extends PureComponent { currency={nativeCurrency} denomination={GWEI} value={gasPrice} + numberOfDecimals={9} hideLabel /> )} From d01b702b23c353ada8c4dc14b0ac72a0cc5c905f Mon Sep 17 00:00:00 2001 From: Mark Stacey Date: Wed, 4 Aug 2021 19:44:46 -0230 Subject: [PATCH 26/55] Update `eth-phishing-detect` to latest version (#11756) This update includes just configuration updates. There are no functional changes. The updated config is only used as a fallback in case the config update fails for some reason. --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index c683c8251..67fd0141a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11459,9 +11459,9 @@ eth-method-registry@^2.0.0: ethjs "^0.4.0" eth-phishing-detect@^1.1.13, eth-phishing-detect@^1.1.14: - version "1.1.14" - resolved "https://registry.yarnpkg.com/eth-phishing-detect/-/eth-phishing-detect-1.1.14.tgz#64dcd35dd3a7a95266d875cbc5280842cda133d7" - integrity sha512-nMQmzrgYabZ52YpuKZ38lStJy9Lozww2WBhyFbu/oepjsZzPvnl2KwJ6TJ78kk14USdl8Htt+eEMk2WAdAjlWg== + version "1.1.15" + resolved "https://registry.yarnpkg.com/eth-phishing-detect/-/eth-phishing-detect-1.1.15.tgz#c42e1aad6cd1c5eeee41c6bf932dcfd0e523d499" + integrity sha512-RVNSGMVIuO6VZ1Uv4v8dljjj0ephW+APVAU5QL5mBu3VEqfBluPMNb6jw66kxYrIFrSNalnb/pMeDpAA+W3cvg== dependencies: fast-levenshtein "^2.0.6" From 818dc13019f01c06925688db9a245860c93c0417 Mon Sep 17 00:00:00 2001 From: Dan J Miller Date: Wed, 4 Aug 2021 19:53:15 -0230 Subject: [PATCH 27/55] Fix legacy unapproved tx handling (#11766) --- ui/hooks/useGasFeeInputs.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/ui/hooks/useGasFeeInputs.js b/ui/hooks/useGasFeeInputs.js index d84af73b9..0f2655b4a 100644 --- a/ui/hooks/useGasFeeInputs.js +++ b/ui/hooks/useGasFeeInputs.js @@ -208,10 +208,15 @@ export function useGasFeeInputs( } = useGasFeeEstimates(); const [initialMaxFeePerGas] = useState( - Number(hexWEIToDecGWEI(transaction?.txParams?.maxFeePerGas)), + networkAndAccountSupports1559 && !transaction?.txParams?.maxFeePerGas + ? Number(hexWEIToDecGWEI(transaction?.txParams?.gasPrice)) + : Number(hexWEIToDecGWEI(transaction?.txParams?.maxFeePerGas)), ); const [initialMaxPriorityFeePerGas] = useState( - Number(hexWEIToDecGWEI(transaction?.txParams?.maxPriorityFeePerGas)), + networkAndAccountSupports1559 && + !transaction?.txParams?.maxPriorityFeePerGas + ? initialMaxFeePerGas + : Number(hexWEIToDecGWEI(transaction?.txParams?.maxPriorityFeePerGas)), ); const [initialGasPrice] = useState( Number(hexWEIToDecGWEI(transaction?.txParams?.gasPrice)), From 9fcd93191b36b08a4e9f79fcd8478d7b8f6cbee3 Mon Sep 17 00:00:00 2001 From: Dan J Miller Date: Wed, 4 Aug 2021 20:14:20 -0230 Subject: [PATCH 28/55] Ensure that gas fee minimum errors show when api is down (#11767) * Fix legacy unapproved tx handling * clean up --- ui/hooks/useGasFeeInputs.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/ui/hooks/useGasFeeInputs.js b/ui/hooks/useGasFeeInputs.js index 0f2655b4a..7c3195fcd 100644 --- a/ui/hooks/useGasFeeInputs.js +++ b/ui/hooks/useGasFeeInputs.js @@ -414,6 +414,17 @@ export function useGasFeeInputs( gasErrors.gasLimit = GAS_FORM_ERRORS.GAS_LIMIT_OUT_OF_BOUNDS; } + // This ensures these are applied when the api fails to return a fee market type + // It is okay if these errors get overwritten below, as those overwrites can only + // happen when the estimate api is live. + if (networkAndAccountSupports1559) { + if (maxPriorityFeePerGasToUse <= 0) { + gasErrors.maxPriorityFee = GAS_FORM_ERRORS.MAX_PRIORITY_FEE_BELOW_MINIMUM; + } else if (maxPriorityFeePerGasToUse >= maxFeePerGasToUse) { + gasErrors.maxFee = GAS_FORM_ERRORS.MAX_FEE_IMBALANCE; + } + } + switch (gasEstimateType) { case GAS_ESTIMATE_TYPES.FEE_MARKET: if (maxPriorityFeePerGasToUse <= 0) { From 01262d33a4fb1f2ec0d7f5cde733484bd5e53f90 Mon Sep 17 00:00:00 2001 From: Dan J Miller Date: Thu, 5 Aug 2021 08:33:49 -0230 Subject: [PATCH 29/55] Ensure that gas fee inputs fallback to tx params values if api is down (#11775) * Ensure that gas fee inputs fallback to tx params values if down * ensure getGasFeeEstimate fallback is a string --- ui/hooks/useGasFeeInputs.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/ui/hooks/useGasFeeInputs.js b/ui/hooks/useGasFeeInputs.js index 7c3195fcd..6ca6da030 100644 --- a/ui/hooks/useGasFeeInputs.js +++ b/ui/hooks/useGasFeeInputs.js @@ -90,11 +90,12 @@ function getGasFeeEstimate( gasFeeEstimates, gasEstimateType, estimateToUse, + fallback = '0', ) { if (gasEstimateType === GAS_ESTIMATE_TYPES.FEE_MARKET) { - return gasFeeEstimates?.[estimateToUse]?.[field] ?? '0'; + return gasFeeEstimates?.[estimateToUse]?.[field] ?? String(fallback); } - return '0'; + return String(fallback); } /** @@ -280,6 +281,7 @@ export function useGasFeeInputs( gasFeeEstimates, gasEstimateType, estimateToUse, + initialMaxFeePerGas, ); const maxPriorityFeePerGasToUse = @@ -289,6 +291,7 @@ export function useGasFeeInputs( gasFeeEstimates, gasEstimateType, estimateToUse, + initialMaxPriorityFeePerGas, ); const [initialGasPriceEstimates] = useState(gasFeeEstimates); From 0cf5019486b5bd20c9404f65043b52142869fcbe Mon Sep 17 00:00:00 2001 From: Dan J Miller Date: Thu, 5 Aug 2021 13:52:56 -0230 Subject: [PATCH 30/55] Use bignumber for number comparisons in useGasFeeInput (#11776) * Use bignumber for number comparisons in useGasFeeInput * fix --- ui/helpers/utils/util.js | 28 ++++++++++++++++++++++++ ui/hooks/useGasFeeInputs.js | 43 ++++++++++++++++++++++++++----------- 2 files changed, 59 insertions(+), 12 deletions(-) diff --git a/ui/helpers/utils/util.js b/ui/helpers/utils/util.js index 23e49d716..9980d9efa 100644 --- a/ui/helpers/utils/util.js +++ b/ui/helpers/utils/util.js @@ -345,3 +345,31 @@ export function constructTxParams({ } return addHexPrefixToObjectValues(txParams); } + +export function bnGreaterThan(a, b) { + if (a === null || a === undefined || b === null || b === undefined) { + return null; + } + return new BigNumber(a, 10).gt(b, 10); +} + +export function bnLessThan(a, b) { + if (a === null || a === undefined || b === null || b === undefined) { + return null; + } + return new BigNumber(a, 10).lt(b, 10); +} + +export function bnGreaterThanEqualTo(a, b) { + if (a === null || a === undefined || b === null || b === undefined) { + return null; + } + return new BigNumber(a, 10).gte(b, 10); +} + +export function bnLessThanEqualTo(a, b) { + if (a === null || a === undefined || b === null || b === undefined) { + return null; + } + return new BigNumber(a, 10).lte(b, 10); +} diff --git a/ui/hooks/useGasFeeInputs.js b/ui/hooks/useGasFeeInputs.js index 6ca6da030..ae3837d9c 100644 --- a/ui/hooks/useGasFeeInputs.js +++ b/ui/hooks/useGasFeeInputs.js @@ -31,6 +31,12 @@ import { hexToDecimal, addHexes, } from '../helpers/utils/conversions.util'; +import { + bnGreaterThan, + bnLessThan, + bnGreaterThanEqualTo, + bnLessThanEqualTo, +} from '../helpers/utils/util'; import { GAS_FORM_ERRORS } from '../helpers/constants/gas'; import { useCurrencyDisplay } from './useCurrencyDisplay'; @@ -421,31 +427,39 @@ export function useGasFeeInputs( // It is okay if these errors get overwritten below, as those overwrites can only // happen when the estimate api is live. if (networkAndAccountSupports1559) { - if (maxPriorityFeePerGasToUse <= 0) { + if (bnLessThanEqualTo(maxPriorityFeePerGasToUse, 0)) { gasErrors.maxPriorityFee = GAS_FORM_ERRORS.MAX_PRIORITY_FEE_BELOW_MINIMUM; - } else if (maxPriorityFeePerGasToUse >= maxFeePerGasToUse) { + } else if ( + bnGreaterThanEqualTo(maxPriorityFeePerGasToUse, maxFeePerGasToUse) + ) { gasErrors.maxFee = GAS_FORM_ERRORS.MAX_FEE_IMBALANCE; } } switch (gasEstimateType) { case GAS_ESTIMATE_TYPES.FEE_MARKET: - if (maxPriorityFeePerGasToUse <= 0) { + if (bnLessThanEqualTo(maxPriorityFeePerGasToUse, 0)) { gasErrors.maxPriorityFee = GAS_FORM_ERRORS.MAX_PRIORITY_FEE_BELOW_MINIMUM; } else if ( !isGasEstimatesLoading && - maxPriorityFeePerGasToUse < - gasFeeEstimates?.low?.suggestedMaxPriorityFeePerGas + bnLessThan( + maxPriorityFeePerGasToUse, + gasFeeEstimates?.low?.suggestedMaxPriorityFeePerGas, + ) ) { gasWarnings.maxPriorityFee = GAS_FORM_ERRORS.MAX_PRIORITY_FEE_TOO_LOW; - } else if (maxPriorityFeePerGasToUse >= maxFeePerGasToUse) { + } else if ( + bnGreaterThanEqualTo(maxPriorityFeePerGasToUse, maxFeePerGasToUse) + ) { gasErrors.maxFee = GAS_FORM_ERRORS.MAX_FEE_IMBALANCE; } else if ( gasFeeEstimates?.high && - maxPriorityFeePerGasToUse > + bnGreaterThan( + maxPriorityFeePerGasToUse, gasFeeEstimates.high.suggestedMaxPriorityFeePerGas * - HIGH_FEE_WARNING_MULTIPLIER + HIGH_FEE_WARNING_MULTIPLIER, + ) ) { gasWarnings.maxPriorityFee = GAS_FORM_ERRORS.MAX_PRIORITY_FEE_HIGH_WARNING; @@ -453,14 +467,19 @@ export function useGasFeeInputs( if ( !isGasEstimatesLoading && - maxFeePerGasToUse < gasFeeEstimates?.low?.suggestedMaxFeePerGas + bnLessThan( + maxFeePerGasToUse, + gasFeeEstimates?.low?.suggestedMaxFeePerGas, + ) ) { gasErrors.maxFee = GAS_FORM_ERRORS.MAX_FEE_TOO_LOW; } else if ( gasFeeEstimates?.high && - maxFeePerGasToUse > + bnGreaterThan( + maxFeePerGasToUse, gasFeeEstimates.high.suggestedMaxFeePerGas * - HIGH_FEE_WARNING_MULTIPLIER + HIGH_FEE_WARNING_MULTIPLIER, + ) ) { gasWarnings.maxFee = GAS_FORM_ERRORS.MAX_FEE_HIGH_WARNING; } @@ -471,7 +490,7 @@ export function useGasFeeInputs( if (networkAndAccountSupports1559) { estimatesUnavailableWarning = true; } - if (gasPriceToUse <= 0) { + if (bnLessThanEqualTo(gasPriceToUse, 0)) { gasErrors.gasPrice = GAS_FORM_ERRORS.GAS_PRICE_TOO_LOW; } break; From 24d6456aafcb622719a04dcc4c57c0ceaf862b82 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 5 Aug 2021 14:12:10 -0230 Subject: [PATCH 31/55] Bump tar from 4.4.11 to 4.4.15 (#11753) Bumps [tar](https://github.com/npm/node-tar) from 4.4.11 to 4.4.15. - [Release notes](https://github.com/npm/node-tar/releases) - [Changelog](https://github.com/npm/node-tar/blob/main/CHANGELOG.md) - [Commits](https://github.com/npm/node-tar/compare/v4.4.11...v4.4.15) --- updated-dependencies: - dependency-name: tar dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- yarn.lock | 73 +++++++++++++++++-------------------------------------- 1 file changed, 22 insertions(+), 51 deletions(-) diff --git a/yarn.lock b/yarn.lock index 67fd0141a..a75eb3829 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8270,9 +8270,9 @@ chokidar@^2.0.0, chokidar@^2.1.1, chokidar@^2.1.8: fsevents "^1.2.7" chownr@^1.1.1: - version "1.1.3" - resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.3.tgz#42d837d5239688d55f303003a508230fa6727142" - integrity sha512-i70fVHhmV3DtTl6nqvZOnIjbY0Pe4kAUjwHj8z0zAdgBtYrJyYwLKCCuRBQ5ppkyL0AkN7HKRnETdmdp1zqNXw== + version "1.1.4" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" + integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== chownr@^2.0.0: version "2.0.0" @@ -13180,11 +13180,11 @@ fs-extra@^9.0.0: universalify "^1.0.0" fs-minipass@^1.2.5: - version "1.2.6" - resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.6.tgz#2c5cc30ded81282bfe8a0d7c7c1853ddeb102c07" - integrity sha512-crhvyXcMejjv3Z5d2Fa9sf5xLYVCF5O1c71QxbVnbLsmYMBEvDAftewesN/HhY03YRoA7zOMxjNGrF5svGaaeQ== + version "1.2.7" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.7.tgz#ccff8570841e7fe4265693da88936c55aed7f7c7" + integrity sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA== dependencies: - minipass "^2.2.1" + minipass "^2.6.0" fs-minipass@^2.0.0: version "2.1.0" @@ -19314,15 +19314,7 @@ minipass-pipeline@^1.2.2: dependencies: minipass "^3.0.0" -minipass@^2.2.1, minipass@^2.6.4: - version "2.6.5" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.6.5.tgz#1c245f9f2897f70fd4a219066261ce6c29f80b18" - integrity sha512-ewSKOPFH9blOLXx0YSE+mbrNMBFPS+11a2b03QZ+P4LVrUHW/GAlqeYC7DBknDyMWkHzrzTpDhUvy7MUxqyrPA== - dependencies: - safe-buffer "^5.1.2" - yallist "^3.0.0" - -minipass@^2.8.6: +minipass@^2.6.0, minipass@^2.8.6, minipass@^2.9.0: version "2.9.0" resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.9.0.tgz#e713762e7d3e32fed803115cf93e04bca9fcc9a6" integrity sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg== @@ -19338,21 +19330,13 @@ minipass@^3.0.0, minipass@^3.1.1: yallist "^4.0.0" minizlib@^1.2.1: - version "1.2.2" - resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.2.2.tgz#6f0ccc82fa53e1bf2ff145f220d2da9fa6e3a166" - integrity sha512-hR3At21uSrsjjDTWrbu0IMLTpnkpv8IIMFDFaoz43Tmu4LkmAXfH44vNNzpTnf+OAQQCHrb91y/wc2J4x5XgSQ== + version "1.3.3" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.3.3.tgz#2290de96818a34c29551c8a8d301216bd65a861d" + integrity sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q== dependencies: - minipass "^2.2.1" + minipass "^2.9.0" -minizlib@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.0.tgz#fd52c645301ef09a63a2c209697c294c6ce02cf3" - integrity sha512-EzTZN/fjSvifSX0SlqUERCN39o6T40AMarPbv0MrarSFtIITCBh7bi+dU8nxGFHuqs9jdIAeoYoKuQAAASsPPA== - dependencies: - minipass "^3.0.0" - yallist "^4.0.0" - -minizlib@^2.1.1: +minizlib@^2.1.0, minizlib@^2.1.1: version "2.1.2" resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== @@ -24470,9 +24454,9 @@ safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1, safe-buffer@~5.1.2: integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2: - version "5.2.0" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519" - integrity sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg== + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== safe-event-emitter@^1.0.1: version "1.0.1" @@ -26318,7 +26302,7 @@ tar-stream@^2.0.0, tar-stream@^2.0.1: inherits "^2.0.3" readable-stream "^3.1.1" -tar@6.0.2, tar@^6.0.2: +tar@6.0.2: version "6.0.2" resolved "https://registry.yarnpkg.com/tar/-/tar-6.0.2.tgz#5df17813468a6264ff14f766886c622b84ae2f39" integrity sha512-Glo3jkRtPcvpDlAs/0+hozav78yoXKFr+c4wgw62NNMO3oo4AaJdCo21Uu7lcwr55h39W2XD1LMERc64wtbItg== @@ -26330,7 +26314,7 @@ tar@6.0.2, tar@^6.0.2: mkdirp "^1.0.3" yallist "^4.0.0" -tar@^4: +tar@^4, tar@^4.0.2: version "4.4.15" resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.15.tgz#3caced4f39ebd46ddda4d6203d48493a919697f8" integrity sha512-ItbufpujXkry7bHH9NpQyTXPbJ72iTlXgkBAYsAjDXk3Ds8t/3NfO5P4xZGy7u+sYuQUbimgzswX4uQIEeNVOA== @@ -26343,20 +26327,7 @@ tar@^4: safe-buffer "^5.1.2" yallist "^3.0.3" -tar@^4.0.2: - version "4.4.11" - resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.11.tgz#7ac09801445a3cf74445ed27499136b5240ffb73" - integrity sha512-iI4zh3ktLJKaDNZKZc+fUONiQrSn9HkCFzamtb7k8FFmVilHVob7QsLX/VySAW8lAviMzMbFw4QtFb4errwgYA== - dependencies: - chownr "^1.1.1" - fs-minipass "^1.2.5" - minipass "^2.6.4" - minizlib "^1.2.1" - mkdirp "^0.5.0" - safe-buffer "^5.1.2" - yallist "^3.0.3" - -tar@^6.1.2: +tar@^6.0.2, tar@^6.1.2: version "6.1.3" resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.3.tgz#e44b97ee7d6cc7a4c574e8b01174614538291825" integrity sha512-3rUqwucgVZXTeyJyL2jqtUau8/8r54SioM1xj3AmTX3HnWQdj2AydfJ2qYYayPyIIznSplcvU9mhBb7dR2XF3w== @@ -28785,9 +28756,9 @@ yallist@^2.1.2: integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI= yallist@^3.0.0, yallist@^3.0.2, yallist@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.0.3.tgz#b4b049e314be545e3ce802236d6cd22cd91c3de9" - integrity sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A== + version "3.1.1" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" + integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== yallist@^4.0.0: version "4.0.0" From e9ef3b168a2de07991deeb930af80e58617b47a3 Mon Sep 17 00:00:00 2001 From: Dan J Miller Date: Thu, 5 Aug 2021 14:15:02 -0230 Subject: [PATCH 32/55] Allow max fee to be equal to max priority fee (#11778) --- ui/hooks/useGasFeeInputs.js | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/ui/hooks/useGasFeeInputs.js b/ui/hooks/useGasFeeInputs.js index ae3837d9c..89a50118d 100644 --- a/ui/hooks/useGasFeeInputs.js +++ b/ui/hooks/useGasFeeInputs.js @@ -34,7 +34,6 @@ import { import { bnGreaterThan, bnLessThan, - bnGreaterThanEqualTo, bnLessThanEqualTo, } from '../helpers/utils/util'; import { GAS_FORM_ERRORS } from '../helpers/constants/gas'; @@ -429,9 +428,7 @@ export function useGasFeeInputs( if (networkAndAccountSupports1559) { if (bnLessThanEqualTo(maxPriorityFeePerGasToUse, 0)) { gasErrors.maxPriorityFee = GAS_FORM_ERRORS.MAX_PRIORITY_FEE_BELOW_MINIMUM; - } else if ( - bnGreaterThanEqualTo(maxPriorityFeePerGasToUse, maxFeePerGasToUse) - ) { + } else if (bnGreaterThan(maxPriorityFeePerGasToUse, maxFeePerGasToUse)) { gasErrors.maxFee = GAS_FORM_ERRORS.MAX_FEE_IMBALANCE; } } @@ -449,9 +446,7 @@ export function useGasFeeInputs( ) ) { gasWarnings.maxPriorityFee = GAS_FORM_ERRORS.MAX_PRIORITY_FEE_TOO_LOW; - } else if ( - bnGreaterThanEqualTo(maxPriorityFeePerGasToUse, maxFeePerGasToUse) - ) { + } else if (bnGreaterThan(maxPriorityFeePerGasToUse, maxFeePerGasToUse)) { gasErrors.maxFee = GAS_FORM_ERRORS.MAX_FEE_IMBALANCE; } else if ( gasFeeEstimates?.high && From d5033249a2b58f784b841df94d7ad12f8edb673f Mon Sep 17 00:00:00 2001 From: David Walsh Date: Thu, 5 Aug 2021 16:52:45 -0500 Subject: [PATCH 33/55] Fix NumericInput proptype error (#11773) --- ui/components/ui/numeric-input/numeric-input.component.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/components/ui/numeric-input/numeric-input.component.js b/ui/components/ui/numeric-input/numeric-input.component.js index 9aa633692..1fe9a18d2 100644 --- a/ui/components/ui/numeric-input/numeric-input.component.js +++ b/ui/components/ui/numeric-input/numeric-input.component.js @@ -40,7 +40,7 @@ export default function NumericInput({ } NumericInput.propTypes = { - value: PropTypes.number, + value: PropTypes.oneOf([PropTypes.number, PropTypes.string]), detailText: PropTypes.string, onChange: PropTypes.func, error: PropTypes.string, From f9378b3aa22451f4d65e9732135754d193ae68d0 Mon Sep 17 00:00:00 2001 From: Alex Donesky Date: Thu, 5 Aug 2021 17:10:04 -0500 Subject: [PATCH 34/55] Add fee level education button to swaps edit gas popover (#11785) --- .../app/edit-gas-popover/edit-gas-popover.component.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ui/components/app/edit-gas-popover/edit-gas-popover.component.js b/ui/components/app/edit-gas-popover/edit-gas-popover.component.js index e77554d17..c213feac3 100644 --- a/ui/components/app/edit-gas-popover/edit-gas-popover.component.js +++ b/ui/components/app/edit-gas-popover/edit-gas-popover.component.js @@ -49,7 +49,9 @@ export default function EditGasPopover({ const shouldAnimate = useShouldAnimateGasEstimations(); const showEducationButton = - mode === EDIT_GAS_MODES.MODIFY_IN_PLACE && networkAndAccountSupport1559; + (mode === EDIT_GAS_MODES.MODIFY_IN_PLACE || + mode === EDIT_GAS_MODES.SWAPS) && + networkAndAccountSupport1559; const [showEducationContent, setShowEducationContent] = useState(false); const [warning] = useState(null); From 804fefcd36b5a11b2b61ecf8cbb6f442aa9a092f Mon Sep 17 00:00:00 2001 From: Alex Donesky Date: Thu, 5 Aug 2021 17:53:48 -0500 Subject: [PATCH 35/55] Separate out non blocking gas errors (#11783) --- app/_locales/en/messages.json | 2 +- ui/hooks/useGasFeeInputs.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index bf51a59ce..dfeedf357 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -704,7 +704,7 @@ "message": "Unknown processing time" }, "editGasTooLowTooltip": { - "message": "Your max fee is low for current market conditions. We don’t know when (or if) your transaction will be processed." + "message": "Your max fee or max priority fee may be low for current market conditions. We don't know when (or if) your transaction will be processed. " }, "editGasTotalBannerSubtitle": { "message": "Up to $1 ($2)", diff --git a/ui/hooks/useGasFeeInputs.js b/ui/hooks/useGasFeeInputs.js index 89a50118d..8dda30e21 100644 --- a/ui/hooks/useGasFeeInputs.js +++ b/ui/hooks/useGasFeeInputs.js @@ -467,7 +467,7 @@ export function useGasFeeInputs( gasFeeEstimates?.low?.suggestedMaxFeePerGas, ) ) { - gasErrors.maxFee = GAS_FORM_ERRORS.MAX_FEE_TOO_LOW; + gasWarnings.maxFee = GAS_FORM_ERRORS.MAX_FEE_TOO_LOW; } else if ( gasFeeEstimates?.high && bnGreaterThan( From a0bd496d56479e130cdd0f42c6babac6cdb89087 Mon Sep 17 00:00:00 2001 From: David Walsh Date: Thu, 5 Aug 2021 18:59:58 -0500 Subject: [PATCH 36/55] Restore heartbeat to transaction confirmation, use isGasEstimatesLoading more broadly (#11781) --- test/jest/mock-store.js | 1 + .../advanced-gas-controls.component.js | 11 ++ .../edit-gas-popover.component.js | 18 ++-- .../app/gas-timing/gas-timing.component.js | 16 +-- .../gas-timing/gas-timing.component.test.js | 100 +++++++++++++----- .../transaction-detail.component.js | 17 ++- ui/components/ui/form-field/form-field.js | 5 + ui/components/ui/loading-heartbeat/index.js | 37 +++---- ui/components/ui/loading-heartbeat/index.scss | 6 +- .../numeric-input/numeric-input.component.js | 3 + ui/ducks/app/app.js | 15 +++ ui/ducks/metamask/metamask.js | 23 ++++ ui/hooks/useGasFeeEstimates.js | 23 +--- ui/hooks/useGasFeeEstimates.test.js | 16 ++- ui/hooks/useShouldAnimateGasEstimations.js | 26 ++++- .../confirm-transaction-base.component.js | 4 +- .../confirm-transaction-base.container.js | 11 +- ui/pages/swaps/fee-card/fee-card.test.js | 40 +++++-- ui/store/actionConstants.js | 2 + 19 files changed, 276 insertions(+), 98 deletions(-) diff --git a/test/jest/mock-store.js b/test/jest/mock-store.js index 0e93e04ee..f57ae13ae 100644 --- a/test/jest/mock-store.js +++ b/test/jest/mock-store.js @@ -234,6 +234,7 @@ export const createSwapsMockStore = () => { }, }, }, + gasLoadingAnimationIsShowing: false, }, }; }; diff --git a/ui/components/app/advanced-gas-controls/advanced-gas-controls.component.js b/ui/components/app/advanced-gas-controls/advanced-gas-controls.component.js index 66ced39b1..75281a2e0 100644 --- a/ui/components/app/advanced-gas-controls/advanced-gas-controls.component.js +++ b/ui/components/app/advanced-gas-controls/advanced-gas-controls.component.js @@ -7,6 +7,8 @@ import FormField from '../../ui/form-field'; import { GAS_ESTIMATE_TYPES } from '../../../../shared/constants/gas'; import { getGasFormErrorText } from '../../../helpers/constants/gas'; import { checkNetworkAndAccountSupports1559 } from '../../../selectors'; +import { getIsGasEstimatesLoading } from '../../../ducks/metamask/metamask'; +import { getGasLoadingAnimationIsShowing } from '../../../ducks/app/app'; export default function AdvancedGasControls({ gasEstimateType, @@ -28,6 +30,12 @@ export default function AdvancedGasControls({ const networkAndAccountSupport1559 = useSelector( checkNetworkAndAccountSupports1559, ); + const isGasEstimatesLoading = useSelector(getIsGasEstimatesLoading); + const isGasLoadingAnimationIsShowing = useSelector( + getGasLoadingAnimationIsShowing, + ); + const disableFormFields = + isGasEstimatesLoading || isGasLoadingAnimationIsShowing; const showFeeMarketFields = networkAndAccountSupport1559 && @@ -71,6 +79,7 @@ export default function AdvancedGasControls({ ? getGasFormErrorText(gasErrors.maxPriorityFee, t) : null } + disabled={disableFormFields} /> ) : ( @@ -107,6 +117,7 @@ export default function AdvancedGasControls({ ? getGasFormErrorText(gasErrors.gasPrice, t) : null } + disabled={disableFormFields} /> )} diff --git a/ui/components/app/edit-gas-popover/edit-gas-popover.component.js b/ui/components/app/edit-gas-popover/edit-gas-popover.component.js index c213feac3..432aee2c8 100644 --- a/ui/components/app/edit-gas-popover/edit-gas-popover.component.js +++ b/ui/components/app/edit-gas-popover/edit-gas-popover.component.js @@ -2,7 +2,7 @@ import React, { useCallback, useContext, useState } from 'react'; import PropTypes from 'prop-types'; import { useDispatch, useSelector } from 'react-redux'; import { useGasFeeInputs } from '../../../hooks/useGasFeeInputs'; -import { useShouldAnimateGasEstimations } from '../../../hooks/useShouldAnimateGasEstimations'; +import { getGasLoadingAnimationIsShowing } from '../../../ducks/app/app'; import { EDIT_GAS_MODES, GAS_LIMITS } from '../../../../shared/constants/gas'; @@ -45,8 +45,9 @@ export default function EditGasPopover({ const networkAndAccountSupport1559 = useSelector( checkNetworkAndAccountSupports1559, ); - - const shouldAnimate = useShouldAnimateGasEstimations(); + const gasLoadingAnimationIsShowing = useSelector( + getGasLoadingAnimationIsShowing, + ); const showEducationButton = (mode === EDIT_GAS_MODES.MODIFY_IN_PLACE || @@ -192,7 +193,12 @@ export default function EditGasPopover({ @@ -205,9 +211,7 @@ export default function EditGasPopover({ ) : ( <> - {process.env.IN_TEST === 'true' ? null : ( - - )} + {process.env.IN_TEST === 'true' ? null : } { + const actual = jest.requireActual('react-redux'); + + return { + ...actual, + useSelector: jest.fn(), + }; +}); + +const DEFAULT_OPTS = { + gasEstimateType: GAS_ESTIMATE_TYPES.FEE_MARKET, + gasFeeEstimates: { + low: '10', + medium: '20', + high: '30', + }, + isGasEstimatesLoading: true, +}; + +const generateUseSelectorRouter = (opts = DEFAULT_OPTS) => (selector) => { + if (selector === checkNetworkAndAccountSupports1559) { + return true; + } + if (selector === getGasEstimateType) { + return opts.gasEstimateType ?? DEFAULT_OPTS.gasEstimateType; + } + if (selector === getGasFeeEstimates) { + return opts.gasFeeEstimates ?? DEFAULT_OPTS.gasFeeEstimates; + } + if (selector === getIsGasEstimatesLoading) { + return opts.isGasEstimatesLoading ?? DEFAULT_OPTS.isGasEstimatesLoading; + } + return undefined; +}; + describe('Gas timing', () => { beforeEach(() => { const useI18nContext = sinon.stub(i18nhooks, 'useI18nContext'); useI18nContext.returns((key, variables) => getMessage('en', messages, key, variables), ); + jest.clearAllMocks(); + useSelector.mockImplementation(generateUseSelectorRouter()); }); afterEach(() => { sinon.restore(); }); it('renders nothing when gas is loading', () => { - sinon.stub(useGasFeeEstimatesExport, 'useGasFeeEstimates').returns({ - isGasEstimatesLoading: true, - gasFeeEstimates: null, - gasEstimateType: GAS_ESTIMATE_TYPES.FEE_MARKET, - }); + useSelector.mockImplementation( + generateUseSelectorRouter({ + isGasEstimatesLoading: true, + gasFeeEstimates: null, + gasEstimateType: GAS_ESTIMATE_TYPES.FEE_MARKET, + }), + ); const wrapper = shallow(); expect(wrapper.html()).toBeNull(); }); it('renders "very likely" when high estimate is chosen', () => { - sinon.stub(useGasFeeEstimatesExport, 'useGasFeeEstimates').returns({ - isGasEstimatesLoading: false, - gasFeeEstimates: MOCK_FEE_ESTIMATE, - gasEstimateType: GAS_ESTIMATE_TYPES.FEE_MARKET, - }); + useSelector.mockImplementation( + generateUseSelectorRouter({ + isGasEstimatesLoading: false, + gasFeeEstimates: MOCK_FEE_ESTIMATE, + gasEstimateType: GAS_ESTIMATE_TYPES.FEE_MARKET, + }), + ); - const wrapper = shallow(); + const wrapper = shallow(); expect(wrapper.html()).toContain('gasTimingVeryPositive'); }); it('renders "likely" when medium estimate is chosen', () => { - sinon.stub(useGasFeeEstimatesExport, 'useGasFeeEstimates').returns({ - isGasEstimatesLoading: false, - gasFeeEstimates: MOCK_FEE_ESTIMATE, - gasEstimateType: GAS_ESTIMATE_TYPES.FEE_MARKET, - }); + useSelector.mockImplementation( + generateUseSelectorRouter({ + isGasEstimatesLoading: false, + gasFeeEstimates: MOCK_FEE_ESTIMATE, + gasEstimateType: GAS_ESTIMATE_TYPES.FEE_MARKET, + }), + ); - const wrapper = shallow(); + const wrapper = shallow(); expect(wrapper.html()).toContain('gasTimingPositive'); }); it('renders "maybe" when low estimate is chosen', () => { - sinon.stub(useGasFeeEstimatesExport, 'useGasFeeEstimates').returns({ - isGasEstimatesLoading: false, - gasFeeEstimates: MOCK_FEE_ESTIMATE, - gasEstimateType: GAS_ESTIMATE_TYPES.FEE_MARKET, - }); + useSelector.mockImplementation( + generateUseSelectorRouter({ + isGasEstimatesLoading: false, + gasFeeEstimates: MOCK_FEE_ESTIMATE, + gasEstimateType: GAS_ESTIMATE_TYPES.FEE_MARKET, + }), + ); - const wrapper = shallow(); + const wrapper = shallow(); expect(wrapper.html()).toContain('gasTimingNegative'); }); }); diff --git a/ui/components/app/transaction-detail/transaction-detail.component.js b/ui/components/app/transaction-detail/transaction-detail.component.js index 0829df684..2f12d89da 100644 --- a/ui/components/app/transaction-detail/transaction-detail.component.js +++ b/ui/components/app/transaction-detail/transaction-detail.component.js @@ -1,18 +1,33 @@ import React, { useContext } from 'react'; import PropTypes from 'prop-types'; +import { useSelector } from 'react-redux'; + +import { getIsGasEstimatesLoading } from '../../../ducks/metamask/metamask'; +import { getGasLoadingAnimationIsShowing } from '../../../ducks/app/app'; import { I18nContext } from '../../../contexts/i18n'; import TransactionDetailItem from '../transaction-detail-item/transaction-detail-item.component'; +import LoadingHeartBeat from '../../ui/loading-heartbeat'; export default function TransactionDetail({ rows = [], onEdit }) { const t = useContext(I18nContext); + const isGasEstimatesLoading = useSelector(getIsGasEstimatesLoading); + const gasLoadingAnimationIsShowing = useSelector( + getGasLoadingAnimationIsShowing, + ); return (
+ {process.env.IN_TEST === 'true' ? null : } {onEdit && (
- +
)}
{rows}
diff --git a/ui/components/ui/form-field/form-field.js b/ui/components/ui/form-field/form-field.js index 9152937a6..d735636ac 100644 --- a/ui/components/ui/form-field/form-field.js +++ b/ui/components/ui/form-field/form-field.js @@ -28,6 +28,7 @@ export default function FormField({ autoFocus, password, allowDecimals, + disabled, }) { return (
) : ( )} {error && ( @@ -120,6 +123,7 @@ FormField.propTypes = { numeric: PropTypes.bool, password: PropTypes.bool, allowDecimals: PropTypes.bool, + disabled: PropTypes.bool, }; FormField.defaultProps = { @@ -135,4 +139,5 @@ FormField.defaultProps = { numeric: false, password: false, allowDecimals: true, + disabled: false, }; diff --git a/ui/components/ui/loading-heartbeat/index.js b/ui/components/ui/loading-heartbeat/index.js index fae2e886d..2c8e491e8 100644 --- a/ui/components/ui/loading-heartbeat/index.js +++ b/ui/components/ui/loading-heartbeat/index.js @@ -1,36 +1,25 @@ import classNames from 'classnames'; -import PropTypes from 'prop-types'; -import React, { useEffect, useRef } from 'react'; +import React from 'react'; +import { useSelector } from 'react-redux'; +import { getGasLoadingAnimationIsShowing } from '../../../ducks/app/app'; +import { useShouldAnimateGasEstimations } from '../../../hooks/useShouldAnimateGasEstimations'; -export default function LoadingHeartBeat({ active }) { - const heartNode = useRef(null); +const BASE_CLASS = 'loading-heartbeat'; +const LOADING_CLASS = `${BASE_CLASS}--active`; - const LOADING_CLASS = 'loading-heartbeat--active'; - - // When the loading animation completes, remove the className to disappear again - useEffect(() => { - const eventName = 'animationend'; - const node = heartNode?.current; - const eventHandler = () => { - node?.classList.remove(LOADING_CLASS); - }; - - node?.addEventListener(eventName, eventHandler); - return () => { - node?.removeEventListener(eventName, eventHandler); - }; - }, [heartNode]); +export default function LoadingHeartBeat() { + useShouldAnimateGasEstimations(); + const active = useSelector(getGasLoadingAnimationIsShowing); return (
{ + e.preventDefault(); + e.stopPropagation(); + }} >
); } - -LoadingHeartBeat.propTypes = { - active: PropTypes.bool, -}; diff --git a/ui/components/ui/loading-heartbeat/index.scss b/ui/components/ui/loading-heartbeat/index.scss index 0b1b095f2..0e3c4d19d 100644 --- a/ui/components/ui/loading-heartbeat/index.scss +++ b/ui/components/ui/loading-heartbeat/index.scss @@ -7,10 +7,14 @@ opacity: 0; background: #fff; display: none; + pointer-events: none; &--active { display: block; - animation: heartbeat 2s ease-in-out; + animation-name: heartbeat; + animation-duration: 2s; + animation-timing-function: ease-in-out; + animation-iteration-count: infinite; } } diff --git a/ui/components/ui/numeric-input/numeric-input.component.js b/ui/components/ui/numeric-input/numeric-input.component.js index 1fe9a18d2..0befdf4f6 100644 --- a/ui/components/ui/numeric-input/numeric-input.component.js +++ b/ui/components/ui/numeric-input/numeric-input.component.js @@ -11,6 +11,7 @@ export default function NumericInput({ error = '', autoFocus = false, allowDecimals = true, + disabled = false, }) { return (
{detailText && ( @@ -46,4 +48,5 @@ NumericInput.propTypes = { error: PropTypes.string, autoFocus: PropTypes.bool, allowDecimals: PropTypes.bool, + disabled: PropTypes.bool, }; diff --git a/ui/ducks/app/app.js b/ui/ducks/app/app.js index e3b3e275f..93a21a16f 100644 --- a/ui/ducks/app/app.js +++ b/ui/ducks/app/app.js @@ -53,6 +53,7 @@ export default function reduceApp(state = {}, action) { singleExceptions: { testKey: null, }, + gasLoadingAnimationIsShowing: false, ...state, }; @@ -358,6 +359,12 @@ export default function reduceApp(state = {}, action) { }, }; + case actionConstants.TOGGLE_GAS_LOADING_ANIMATION: + return { + ...appState, + gasLoadingAnimationIsShowing: action.value, + }; + default: return appState; } @@ -377,7 +384,15 @@ export function hideWhatsNewPopup() { }; } +export function toggleGasLoadingAnimation(value) { + return { type: actionConstants.TOGGLE_GAS_LOADING_ANIMATION, value }; +} + // Selectors export function getQrCodeData(state) { return state.appState.qrCodeData; } + +export function getGasLoadingAnimationIsShowing(state) { + return state.appState.gasLoadingAnimationIsShowing; +} diff --git a/ui/ducks/metamask/metamask.js b/ui/ducks/metamask/metamask.js index 8d93677a1..b2bef9558 100644 --- a/ui/ducks/metamask/metamask.js +++ b/ui/ducks/metamask/metamask.js @@ -4,11 +4,13 @@ import { ALERT_TYPES } from '../../../shared/constants/alerts'; import { NETWORK_TYPE_RPC } from '../../../shared/constants/network'; import { accountsWithSendEtherInfoSelector, + checkNetworkAndAccountSupports1559, getAddressBook, } from '../../selectors'; import { updateTransaction } from '../../store/actions'; import { setCustomGasLimit, setCustomGasPrice } from '../gas/gas.duck'; import { decGWEIToHexWEI } from '../../helpers/utils/conversions.util'; +import { GAS_ESTIMATE_TYPES } from '../../../shared/constants/gas'; export default function reduceMetamask(state = {}, action) { const metamaskState = { @@ -299,3 +301,24 @@ export function getGasFeeEstimates(state) { export function getEstimatedGasFeeTimeBounds(state) { return state.metamask.estimatedGasFeeTimeBounds; } + +export function getIsGasEstimatesLoading(state) { + const networkAndAccountSupports1559 = checkNetworkAndAccountSupports1559( + state, + ); + const gasEstimateType = getGasEstimateType(state); + + // We consider the gas estimate to be loading if the gasEstimateType is + // 'NONE' or if the current gasEstimateType cannot be supported by the current + // network + const isEIP1559TolerableEstimateType = + gasEstimateType === GAS_ESTIMATE_TYPES.FEE_MARKET || + gasEstimateType === GAS_ESTIMATE_TYPES.ETH_GASPRICE; + const isGasEstimatesLoading = + gasEstimateType === GAS_ESTIMATE_TYPES.NONE || + (networkAndAccountSupports1559 && !isEIP1559TolerableEstimateType) || + (!networkAndAccountSupports1559 && + gasEstimateType === GAS_ESTIMATE_TYPES.FEE_MARKET); + + return isGasEstimatesLoading; +} diff --git a/ui/hooks/useGasFeeEstimates.js b/ui/hooks/useGasFeeEstimates.js index 094e575f9..7bbdd4fc0 100644 --- a/ui/hooks/useGasFeeEstimates.js +++ b/ui/hooks/useGasFeeEstimates.js @@ -1,17 +1,12 @@ import { useSelector } from 'react-redux'; -import { GAS_ESTIMATE_TYPES } from '../../shared/constants/gas'; import { getEstimatedGasFeeTimeBounds, getGasEstimateType, getGasFeeEstimates, + getIsGasEstimatesLoading, } from '../ducks/metamask/metamask'; -import { checkNetworkAndAccountSupports1559 } from '../selectors'; import { useSafeGasEstimatePolling } from './useSafeGasEstimatePolling'; -/** - * @typedef {keyof typeof GAS_ESTIMATE_TYPES} GasEstimateTypes - */ - /** * @typedef {object} GasEstimates * @property {GasEstimateTypes} gasEstimateType - The type of estimate provided @@ -35,26 +30,12 @@ import { useSafeGasEstimatePolling } from './useSafeGasEstimatePolling'; * @returns {GasFeeEstimates} - GasFeeEstimates object */ export function useGasFeeEstimates() { - const networkAndAccountSupports1559 = useSelector( - checkNetworkAndAccountSupports1559, - ); const gasEstimateType = useSelector(getGasEstimateType); const gasFeeEstimates = useSelector(getGasFeeEstimates); const estimatedGasFeeTimeBounds = useSelector(getEstimatedGasFeeTimeBounds); + const isGasEstimatesLoading = useSelector(getIsGasEstimatesLoading); useSafeGasEstimatePolling(); - // We consider the gas estimate to be loading if the gasEstimateType is - // 'NONE' or if the current gasEstimateType cannot be supported by the current - // network - const isEIP1559TolerableEstimateType = - gasEstimateType === GAS_ESTIMATE_TYPES.FEE_MARKET || - gasEstimateType === GAS_ESTIMATE_TYPES.ETH_GASPRICE; - const isGasEstimatesLoading = - gasEstimateType === GAS_ESTIMATE_TYPES.NONE || - (networkAndAccountSupports1559 && !isEIP1559TolerableEstimateType) || - (!networkAndAccountSupports1559 && - gasEstimateType === GAS_ESTIMATE_TYPES.FEE_MARKET); - return { gasFeeEstimates, gasEstimateType, diff --git a/ui/hooks/useGasFeeEstimates.test.js b/ui/hooks/useGasFeeEstimates.test.js index 0af584e1c..574bb7904 100644 --- a/ui/hooks/useGasFeeEstimates.test.js +++ b/ui/hooks/useGasFeeEstimates.test.js @@ -5,12 +5,14 @@ import createRandomId from '../../shared/modules/random-id'; import { getGasEstimateType, getGasFeeEstimates, + getIsGasEstimatesLoading, } from '../ducks/metamask/metamask'; import { checkNetworkAndAccountSupports1559 } from '../selectors'; import { disconnectGasFeeEstimatePoller, getGasFeeEstimatesAndStartPolling, } from '../store/actions'; + import { useGasFeeEstimates } from './useGasFeeEstimates'; jest.mock('../store/actions', () => ({ @@ -37,6 +39,7 @@ const DEFAULT_OPTS = { medium: '20', high: '30', }, + isGasEstimatesLoading: true, }; const generateUseSelectorRouter = (opts = DEFAULT_OPTS) => (selector) => { @@ -52,6 +55,9 @@ const generateUseSelectorRouter = (opts = DEFAULT_OPTS) => (selector) => { if (selector === getGasFeeEstimates) { return opts.gasFeeEstimates ?? DEFAULT_OPTS.gasFeeEstimates; } + if (selector === getIsGasEstimatesLoading) { + return opts.isGasEstimatesLoading ?? DEFAULT_OPTS.isGasEstimatesLoading; + } return undefined; }; @@ -68,15 +74,16 @@ describe('useGasFeeEstimates', () => { disconnectGasFeeEstimatePoller.mockImplementation((token) => { tokens = tokens.filter((tkn) => tkn !== token); }); - useSelector.mockImplementation(generateUseSelectorRouter()); }); it('registers with the controller', () => { + useSelector.mockImplementation(generateUseSelectorRouter()); renderHook(() => useGasFeeEstimates()); expect(tokens).toHaveLength(1); }); it('clears token with the controller on unmount', async () => { + useSelector.mockImplementation(generateUseSelectorRouter()); renderHook(() => useGasFeeEstimates()); expect(tokens).toHaveLength(1); const expectedToken = tokens[0]; @@ -87,6 +94,11 @@ describe('useGasFeeEstimates', () => { }); it('works with LEGACY gas prices', () => { + useSelector.mockImplementation( + generateUseSelectorRouter({ + isGasEstimatesLoading: false, + }), + ); const { result: { current }, } = renderHook(() => useGasFeeEstimates()); @@ -104,6 +116,7 @@ describe('useGasFeeEstimates', () => { generateUseSelectorRouter({ gasEstimateType: GAS_ESTIMATE_TYPES.ETH_GASPRICE, gasFeeEstimates, + isGasEstimatesLoading: false, }), ); @@ -145,6 +158,7 @@ describe('useGasFeeEstimates', () => { checkNetworkAndAccountSupports1559: true, gasEstimateType: GAS_ESTIMATE_TYPES.FEE_MARKET, gasFeeEstimates, + isGasEstimatesLoading: false, }), ); diff --git a/ui/hooks/useShouldAnimateGasEstimations.js b/ui/hooks/useShouldAnimateGasEstimations.js index df588c0ec..cfd150c30 100644 --- a/ui/hooks/useShouldAnimateGasEstimations.js +++ b/ui/hooks/useShouldAnimateGasEstimations.js @@ -1,10 +1,20 @@ -import { useRef } from 'react'; +import { useRef, useEffect } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; import { isEqual } from 'lodash'; +import { + getGasLoadingAnimationIsShowing, + toggleGasLoadingAnimation, +} from '../ducks/app/app'; import { useGasFeeEstimates } from './useGasFeeEstimates'; export function useShouldAnimateGasEstimations() { const { isGasEstimatesLoading, gasFeeEstimates } = useGasFeeEstimates(); + const dispatch = useDispatch(); + + const isGasLoadingAnimationActive = useSelector( + getGasLoadingAnimationIsShowing, + ); // Do the animation only when gas prices have changed... const lastGasEstimates = useRef(gasFeeEstimates); @@ -24,5 +34,17 @@ export function useShouldAnimateGasEstimations() { const showLoadingAnimation = isGasEstimatesLoading || (gasEstimatesChanged && !gasJustLoaded); - return showLoadingAnimation; + useEffect(() => { + if ( + isGasLoadingAnimationActive === false && + showLoadingAnimation === true + ) { + dispatch(toggleGasLoadingAnimation(true)); + + setTimeout(() => { + console.log('Killing the toggleGasLoadingAnimation to false'); + dispatch(toggleGasLoadingAnimation(false)); + }, 2000); + } + }, [dispatch, isGasLoadingAnimationActive, showLoadingAnimation]); } diff --git a/ui/pages/confirm-transaction-base/confirm-transaction-base.component.js b/ui/pages/confirm-transaction-base/confirm-transaction-base.component.js index 164573169..f1bb14ba5 100644 --- a/ui/pages/confirm-transaction-base/confirm-transaction-base.component.js +++ b/ui/pages/confirm-transaction-base/confirm-transaction-base.component.js @@ -107,6 +107,7 @@ export default class ConfirmTransactionBase extends Component { setDefaultHomeActiveTabName: PropTypes.func, primaryTotalTextOverride: PropTypes.string, secondaryTotalTextOverride: PropTypes.string, + gasIsLoading: PropTypes.bool, }; state = { @@ -772,6 +773,7 @@ export default class ConfirmTransactionBase extends Component { hideSenderToRecipient, showAccountInHeader, txData, + gasIsLoading, } = this.props; const { submitting, @@ -838,7 +840,7 @@ export default class ConfirmTransactionBase extends Component { lastTx={lastTx} ofText={ofText} requestsWaitingText={requestsWaitingText} - disabled={!valid || submitting} + disabled={!valid || submitting || gasIsLoading} onEdit={() => this.handleEdit()} onCancelAll={() => this.handleCancelAll()} onCancel={() => this.handleCancel()} diff --git a/ui/pages/confirm-transaction-base/confirm-transaction-base.container.js b/ui/pages/confirm-transaction-base/confirm-transaction-base.container.js index 90d349ad8..561ee28ec 100644 --- a/ui/pages/confirm-transaction-base/confirm-transaction-base.container.js +++ b/ui/pages/confirm-transaction-base/confirm-transaction-base.container.js @@ -32,7 +32,11 @@ import { import { getMostRecentOverviewPage } from '../../ducks/history/history'; import { transactionMatchesNetwork } from '../../../shared/modules/transaction.utils'; import { toChecksumHexAddress } from '../../../shared/modules/hexstring-utils'; -import { updateTransactionGasFees } from '../../ducks/metamask/metamask'; +import { + updateTransactionGasFees, + getIsGasEstimatesLoading, +} from '../../ducks/metamask/metamask'; +import { getGasLoadingAnimationIsShowing } from '../../ducks/app/app'; import ConfirmTransactionBase from './confirm-transaction-base.component'; const casedContractMap = Object.keys(contractMap).reduce((acc, base) => { @@ -60,6 +64,10 @@ const mapStateToProps = (state, ownProps) => { const { id: paramsTransactionId } = params; const isMainnet = getIsMainnet(state); const supportsEIP1599 = checkNetworkAndAccountSupports1559(state); + + const isGasEstimatesLoading = getIsGasEstimatesLoading(state); + const gasLoadingAnimationIsShowing = getGasLoadingAnimationIsShowing(state); + const { confirmTransaction, metamask } = state; const { ensResolutionsByAddress, @@ -185,6 +193,7 @@ const mapStateToProps = (state, ownProps) => { isEthGasPrice, noGasPrice, supportsEIP1599, + gasIsLoading: isGasEstimatesLoading || gasLoadingAnimationIsShowing, }; }; diff --git a/ui/pages/swaps/fee-card/fee-card.test.js b/ui/pages/swaps/fee-card/fee-card.test.js index b6183fcca..9327ad69e 100644 --- a/ui/pages/swaps/fee-card/fee-card.test.js +++ b/ui/pages/swaps/fee-card/fee-card.test.js @@ -1,6 +1,14 @@ import React from 'react'; import configureMockStore from 'redux-mock-store'; import thunk from 'redux-thunk'; +import { useSelector } from 'react-redux'; + +import { checkNetworkAndAccountSupports1559 } from '../../../selectors'; +import { + getGasEstimateType, + getGasFeeEstimates, + getIsGasEstimatesLoading, +} from '../../../ducks/metamask/metamask'; import { renderWithProvider, @@ -13,21 +21,34 @@ import FeeCard from '.'; const middleware = [thunk]; -jest.mock('../../../hooks/useGasFeeEstimates', () => { +jest.mock('react-redux', () => { + const actual = jest.requireActual('react-redux'); + return { - useGasFeeEstimates: () => { - return { - gasFeeEstimates: MOCKS.createGasFeeEstimatesForFeeMarket(), - gasEstimateType: 'fee-market', - estimatedGasFeeTimeBounds: undefined, - isGasEstimatesLoading: false, - }; - }, + ...actual, + useSelector: jest.fn(), }; }); +const generateUseSelectorRouter = () => (selector) => { + if (selector === checkNetworkAndAccountSupports1559) { + return true; + } + if (selector === getGasEstimateType) { + return 'fee-market'; + } + if (selector === getGasFeeEstimates) { + return MOCKS.createGasFeeEstimatesForFeeMarket(); + } + if (selector === getIsGasEstimatesLoading) { + return false; + } + return undefined; +}; + setBackgroundConnection({ getGasFeeTimeEstimate: jest.fn(), + getGasFeeEstimatesAndStartPolling: jest.fn(), }); const createProps = (customProps = {}) => { @@ -65,6 +86,7 @@ const createProps = (customProps = {}) => { describe('FeeCard', () => { it('renders the component with initial props', () => { + useSelector.mockImplementation(generateUseSelectorRouter()); const props = createProps(); const { getByText } = renderWithProvider(); expect(getByText('Using the best quote')).toBeInTheDocument(); diff --git a/ui/store/actionConstants.js b/ui/store/actionConstants.js index 8046ea05d..d62e3f5b1 100644 --- a/ui/store/actionConstants.js +++ b/ui/store/actionConstants.js @@ -102,3 +102,5 @@ export const SET_OPEN_METAMASK_TAB_IDS = 'SET_OPEN_METAMASK_TAB_IDS'; // Home Screen export const HIDE_WHATS_NEW_POPUP = 'HIDE_WHATS_NEW_POPUP'; + +export const TOGGLE_GAS_LOADING_ANIMATION = 'TOGGLE_GAS_LOADING_ANIMATION'; From 751534e66502c88202d8e222864d5cf71b5b93f0 Mon Sep 17 00:00:00 2001 From: Alex Donesky Date: Thu, 5 Aug 2021 20:07:04 -0500 Subject: [PATCH 37/55] fix confirm transaction details to match spec (#11779) --- app/_locales/en/messages.json | 14 +- ...onfirm-token-transaction-base.component.js | 13 +- ...onfirm-token-transaction-base.container.js | 22 ++- .../confirm-transaction-base.component.js | 154 +++++++++++------- .../confirm-transaction-base.container.js | 4 +- 5 files changed, 134 insertions(+), 73 deletions(-) diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index dfeedf357..27deab5fc 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -690,11 +690,19 @@ "message": "This network requires a “Gas price” field when submitting a transaction. Gas price is the amount you will pay pay per unit of gas." }, "editGasSubTextAmount": { - "message": "Max amount: $1", - "description": "$1 represents a dollar amount" + "message": "$1 $2", + "description": "$1 will be passed the editGasSubTextAmountLabel and $2 will be passed the amount in either cryptocurrency or fiat" + }, + "editGasSubTextAmountLabel": { + "message": "Max amount:", + "description": "This is meant to be used as the $1 substitution editGasSubTextAmount" }, "editGasSubTextFee": { - "message": "Max fee: $1", + "message": "$1 $2", + "description": "$1 will be passed the editGasSubTextFeeLabel and $2 will be passed the fee amount in either cryptocurrency or fiat" + }, + "editGasSubTextFeeLabel": { + "message": "Max fee:", "description": "$1 represents a dollar amount" }, "editGasTitle": { diff --git a/ui/pages/confirm-token-transaction-base/confirm-token-transaction-base.component.js b/ui/pages/confirm-token-transaction-base/confirm-token-transaction-base.component.js index 557b6c237..90180b23e 100644 --- a/ui/pages/confirm-token-transaction-base/confirm-token-transaction-base.component.js +++ b/ui/pages/confirm-token-transaction-base/confirm-token-transaction-base.component.js @@ -20,9 +20,11 @@ export default function ConfirmTokenTransactionBase({ tokenSymbol, fiatTransactionTotal, ethTransactionTotal, + ethTransactionTotalMaxAmount, contractExchangeRate, conversionRate, currentCurrency, + nativeCurrency, onEdit, }) { const t = useContext(I18nContext); @@ -85,13 +87,8 @@ export default function ConfirmTokenTransactionBase({ /> ) } - primaryTotalTextOverride={ -
- {`${tokensText} + `} - - {ethTransactionTotal} -
- } + primaryTotalTextOverride={`${tokensText} + ${ethTransactionTotal} ${nativeCurrency}`} + primaryTotalTextOverrideMaxAmount={`${tokensText} + ${ethTransactionTotalMaxAmount} ${nativeCurrency}`} secondaryTotalTextOverride={secondaryTotalTextOverride} /> ); @@ -108,4 +105,6 @@ ConfirmTokenTransactionBase.propTypes = { conversionRate: PropTypes.number, currentCurrency: PropTypes.string, onEdit: PropTypes.func, + nativeCurrency: PropTypes.string, + ethTransactionTotalMaxAmount: PropTypes.string, }; diff --git a/ui/pages/confirm-token-transaction-base/confirm-token-transaction-base.container.js b/ui/pages/confirm-token-transaction-base/confirm-token-transaction-base.container.js index 2d7bbde85..58c093bea 100644 --- a/ui/pages/confirm-token-transaction-base/confirm-token-transaction-base.container.js +++ b/ui/pages/confirm-token-transaction-base/confirm-token-transaction-base.container.js @@ -12,6 +12,8 @@ import { getTokenAddressParam, getTokenValueParam, } from '../../helpers/utils/token-util'; +import { hexWEIToDecETH } from '../../helpers/utils/conversions.util'; +import { getHexGasTotal } from '../../helpers/utils/confirm-tx.util'; import ConfirmTokenTransactionBase from './confirm-token-transaction-base.component'; const mapStateToProps = (state, ownProps) => { @@ -21,16 +23,30 @@ const mapStateToProps = (state, ownProps) => { const { id: paramsTransactionId } = params; const { confirmTransaction, - metamask: { currentCurrency, conversionRate, currentNetworkTxList }, + metamask: { + currentCurrency, + conversionRate, + currentNetworkTxList, + nativeCurrency, + }, } = state; const { txData: { id: transactionId, - txParams: { to: tokenAddress, data } = {}, + txParams: { to: tokenAddress, data, maxFeePerGas, gasPrice, gas } = {}, } = {}, } = confirmTransaction; + const ethTransactionTotalMaxAmount = Number( + hexWEIToDecETH( + getHexGasTotal({ + gasPrice: maxFeePerGas ?? gasPrice, + gasLimit: gas, + }), + ), + ).toFixed(6); + const transaction = currentNetworkTxList.find( ({ id }) => id === (Number(paramsTransactionId) || transactionId), @@ -61,6 +77,8 @@ const mapStateToProps = (state, ownProps) => { contractExchangeRate, fiatTransactionTotal, ethTransactionTotal, + ethTransactionTotalMaxAmount, + nativeCurrency, }; }; diff --git a/ui/pages/confirm-transaction-base/confirm-transaction-base.component.js b/ui/pages/confirm-transaction-base/confirm-transaction-base.component.js index f1bb14ba5..c26671323 100644 --- a/ui/pages/confirm-transaction-base/confirm-transaction-base.component.js +++ b/ui/pages/confirm-transaction-base/confirm-transaction-base.component.js @@ -108,6 +108,8 @@ export default class ConfirmTransactionBase extends Component { primaryTotalTextOverride: PropTypes.string, secondaryTotalTextOverride: PropTypes.string, gasIsLoading: PropTypes.bool, + primaryTotalTextOverrideMaxAmount: PropTypes.string, + useNativeCurrencyAsPrimaryCurrency: PropTypes.bool, }; state = { @@ -280,6 +282,9 @@ export default class ConfirmTransactionBase extends Component { nextNonce, getNextNonce, txData, + useNativeCurrencyAsPrimaryCurrency, + hexMaximumTransactionFee, + primaryTotalTextOverrideMaxAmount, } = this.props; const { t } = this.context; @@ -291,6 +296,70 @@ export default class ConfirmTransactionBase extends Component { } }; + const renderTotalMaxAmount = () => { + if ( + primaryTotalTextOverrideMaxAmount === undefined && + secondaryTotalTextOverride === undefined + ) { + // Native Send + return ( + + ); + } + + // Token send + return useNativeCurrencyAsPrimaryCurrency + ? primaryTotalTextOverrideMaxAmount + : secondaryTotalTextOverride; + }; + + const renderTotalDetailTotal = () => { + if ( + primaryTotalTextOverride === undefined && + secondaryTotalTextOverride === undefined + ) { + return ( + + ); + } + return useNativeCurrencyAsPrimaryCurrency + ? primaryTotalTextOverride + : secondaryTotalTextOverride; + }; + + const renderTotalDetailText = () => { + if ( + primaryTotalTextOverride === undefined && + secondaryTotalTextOverride === undefined + ) { + return ( + + ); + } + return useNativeCurrencyAsPrimaryCurrency + ? secondaryTotalTextOverride + : primaryTotalTextOverride; + }; + const nonceField = useNonceField ? (
@@ -372,35 +441,29 @@ export default class ConfirmTransactionBase extends Component { } detailText={ } detailTotal={ } - subText={ - - {t('editGasSubTextFee', [ - , - ])} - - } + subText={t('editGasSubTextFee', [ + + {t('editGasSubTextFeeLabel')} + , + , + ])} subTitle={ , - } - detailTotal={ - - } - subTitle={ - secondaryTotalTextOverride || - t('transactionDetailGasTotalSubtitle') - } - subText={ - - {t('editGasSubTextAmount', [ - , - ])} - - } + detailTitle={t('total')} + detailText={renderTotalDetailText()} + detailTotal={renderTotalDetailTotal()} + subTitle={t('transactionDetailGasTotalSubtitle')} + subText={t('editGasSubTextAmount', [ + + {t('editGasSubTextAmountLabel')} + , + renderTotalMaxAmount(), + ])} />, ]} /> diff --git a/ui/pages/confirm-transaction-base/confirm-transaction-base.container.js b/ui/pages/confirm-transaction-base/confirm-transaction-base.container.js index 561ee28ec..4c8120553 100644 --- a/ui/pages/confirm-transaction-base/confirm-transaction-base.container.js +++ b/ui/pages/confirm-transaction-base/confirm-transaction-base.container.js @@ -28,6 +28,7 @@ import { getIsEthGasPriceFetched, getShouldShowFiat, checkNetworkAndAccountSupports1559, + getPreferences, } from '../../selectors'; import { getMostRecentOverviewPage } from '../../ducks/history/history'; import { transactionMatchesNetwork } from '../../../shared/modules/transaction.utils'; @@ -152,7 +153,7 @@ const mapStateToProps = (state, ownProps) => { customNonceValue = getCustomNonceValue(state); const isEthGasPrice = getIsEthGasPriceFetched(state); const noGasPrice = getNoGasPriceFetched(state); - + const { useNativeCurrencyAsPrimaryCurrency } = getPreferences(state); return { balance, fromAddress, @@ -194,6 +195,7 @@ const mapStateToProps = (state, ownProps) => { noGasPrice, supportsEIP1599, gasIsLoading: isGasEstimatesLoading || gasLoadingAnimationIsShowing, + useNativeCurrencyAsPrimaryCurrency, }; }; From 1b1d039310e8e8f6c95e8e8f97dd75df1123026d Mon Sep 17 00:00:00 2001 From: David Walsh Date: Thu, 5 Aug 2021 20:25:23 -0500 Subject: [PATCH 38/55] EIP-1559 - Ensure form always displays when there are errors (#11787) --- .../app/edit-gas-display/edit-gas-display.component.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ui/components/app/edit-gas-display/edit-gas-display.component.js b/ui/components/app/edit-gas-display/edit-gas-display.component.js index 22216a2a7..28dc11458 100644 --- a/ui/components/app/edit-gas-display/edit-gas-display.component.js +++ b/ui/components/app/edit-gas-display/edit-gas-display.component.js @@ -77,7 +77,7 @@ export default function EditGasDisplay({ ); const [showAdvancedForm, setShowAdvancedForm] = useState( - !estimateToUse || hasGasErrors || !networkAndAccountSupport1559, + !estimateToUse || !networkAndAccountSupport1559, ); const [hideRadioButtons, setHideRadioButtons] = useState( showAdvancedInlineGasIfPossible, @@ -241,7 +241,9 @@ export default function EditGasDisplay({ )} {!requireDappAcknowledgement && - (showAdvancedForm || showAdvancedInlineGasIfPossible) && ( + (showAdvancedForm || + hasGasErrors || + showAdvancedInlineGasIfPossible) && ( Date: Fri, 6 Aug 2021 15:52:28 +0200 Subject: [PATCH 39/55] Fix fee level content (#11790) --- app/_locales/en/messages.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index 27deab5fc..4966e16b9 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -633,7 +633,7 @@ "message": "This is best for swaps or other time sensitive transactions. If a swap takes too long to process it will often fail and you may lose funds." }, "editGasEducationLowExplanation": { - "message": "A lower gas fee should only be selected for transactions where processing time is less important. With a lower fee, it can be be hard to predict when (or if) your transaction with be successful." + "message": "A lower gas fee should only be selected for transactions where processing time is less important. With a lower fee, it can be hard to predict when (or if) your transaction will be successful." }, "editGasEducationMediumExplanation": { "message": "A medium gas fee is good for sending, withdrawing or other non-time sensitive but important transactions." From bfde5a1d776c8e41a8c99e1e4e355949fc61ac9e Mon Sep 17 00:00:00 2001 From: Dan J Miller Date: Fri, 6 Aug 2021 17:01:30 -0230 Subject: [PATCH 40/55] Fixes updates on the confirm screen. (#11788) * Fixes updates on the confirm screen. * Better handling of internal send transactions * maxFee -> maxFeePerGas property name fix * Remove redundant setEstimateToUse call in onManualChange * Fix unit tests * rebase error fix * Fixes to speedup loading and transaction breakdown priority fee * Fix lint and unit tests * Ensure gas price based transaction that have been customized (e.g. speed up and retry) are properly initialized in useGasFeeInputs * Clean up * Link fix --- app/scripts/controllers/swaps.js | 8 ++ app/scripts/controllers/swaps.test.js | 1 + app/scripts/controllers/transactions/index.js | 13 +++ app/scripts/metamask-controller.js | 4 + .../advanced-gas-controls.component.js | 3 +- .../edit-gas-display.component.js | 10 ++- .../edit-gas-popover.component.js | 22 +++-- .../transaction-list-item.component.js | 2 + ui/ducks/swaps/swaps.js | 3 + ui/helpers/utils/conversions.util.js | 2 +- ui/hooks/useGasFeeInputs.js | 90 +++++++------------ ...onfirm-token-transaction-base.container.js | 25 +++--- .../confirm-transaction-base.component.js | 39 +++++--- .../confirm-transaction-base.container.js | 6 +- ui/pages/swaps/view-quote/view-quote.js | 7 +- ui/selectors/confirm-transaction.js | 25 ++++-- ui/store/actions.js | 7 ++ 17 files changed, 156 insertions(+), 111 deletions(-) diff --git a/app/scripts/controllers/swaps.js b/app/scripts/controllers/swaps.js index 1615b2594..238dba04c 100644 --- a/app/scripts/controllers/swaps.js +++ b/app/scripts/controllers/swaps.js @@ -73,6 +73,7 @@ const initialState = { customGasPrice: null, customMaxFeePerGas: null, customMaxPriorityFeePerGas: null, + swapsUserFeeLevel: '', selectedAggId: null, customApproveTxData: '', errorKey: '', @@ -456,6 +457,13 @@ export default class SwapsController { }); } + setSwapsUserFeeLevel(swapsUserFeeLevel) { + const { swapsState } = this.store.getState(); + this.store.updateState({ + swapsState: { ...swapsState, swapsUserFeeLevel }, + }); + } + setSwapsTxMaxFeePriorityPerGas(maxPriorityFeePerGas) { const { swapsState } = this.store.getState(); this.store.updateState({ diff --git a/app/scripts/controllers/swaps.test.js b/app/scripts/controllers/swaps.test.js index 25d7738aa..4ed83ef25 100644 --- a/app/scripts/controllers/swaps.test.js +++ b/app/scripts/controllers/swaps.test.js @@ -133,6 +133,7 @@ const EMPTY_INIT_STATE = { swapsFeatureIsLive: true, useNewSwapsApi: false, swapsQuoteRefreshTime: 60000, + swapsUserFeeLevel: '', }, }; diff --git a/app/scripts/controllers/transactions/index.js b/app/scripts/controllers/transactions/index.js index af85a0310..9afa217e1 100644 --- a/app/scripts/controllers/transactions/index.js +++ b/app/scripts/controllers/transactions/index.js @@ -433,7 +433,20 @@ export default class TransactionController extends EventEmitter { ) { txMeta.txParams.maxFeePerGas = txMeta.txParams.gasPrice; txMeta.txParams.maxPriorityFeePerGas = txMeta.txParams.gasPrice; + txMeta.userFeeLevel = 'custom'; } else { + if ( + (defaultMaxFeePerGas && + defaultMaxPriorityFeePerGas && + !txMeta.txParams.maxFeePerGas && + !txMeta.txParams.maxPriorityFeePerGas) || + txMeta.origin === 'metamask' + ) { + txMeta.userFeeLevel = 'medium'; + } else { + txMeta.userFeeLevel = 'custom'; + } + if (defaultMaxFeePerGas && !txMeta.txParams.maxFeePerGas) { // If the dapp has not set the gasPrice or the maxFeePerGas, then we set maxFeePerGas // with the one returned by the gasFeeController, if that is available. diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index f1c2cba85..adef54dbb 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -1089,6 +1089,10 @@ export default class MetamaskController extends EventEmitter { swapsController.setSwapsLiveness, swapsController, ), + setSwapsUserFeeLevel: nodeify( + swapsController.setSwapsUserFeeLevel, + swapsController, + ), // MetaMetrics trackMetaMetricsEvent: nodeify( diff --git a/ui/components/app/advanced-gas-controls/advanced-gas-controls.component.js b/ui/components/app/advanced-gas-controls/advanced-gas-controls.component.js index 75281a2e0..ee9f3429d 100644 --- a/ui/components/app/advanced-gas-controls/advanced-gas-controls.component.js +++ b/ui/components/app/advanced-gas-controls/advanced-gas-controls.component.js @@ -40,7 +40,8 @@ export default function AdvancedGasControls({ const showFeeMarketFields = networkAndAccountSupport1559 && (gasEstimateType === GAS_ESTIMATE_TYPES.FEE_MARKET || - gasEstimateType === GAS_ESTIMATE_TYPES.ETH_GASPRICE); + gasEstimateType === GAS_ESTIMATE_TYPES.ETH_GASPRICE || + isGasEstimatesLoading); return (
diff --git a/ui/components/app/edit-gas-display/edit-gas-display.component.js b/ui/components/app/edit-gas-display/edit-gas-display.component.js index 28dc11458..26c15ad9c 100644 --- a/ui/components/app/edit-gas-display/edit-gas-display.component.js +++ b/ui/components/app/edit-gas-display/edit-gas-display.component.js @@ -77,7 +77,9 @@ export default function EditGasDisplay({ ); const [showAdvancedForm, setShowAdvancedForm] = useState( - !estimateToUse || !networkAndAccountSupport1559, + !estimateToUse || + estimateToUse === 'custom' || + !networkAndAccountSupport1559, ); const [hideRadioButtons, setHideRadioButtons] = useState( showAdvancedInlineGasIfPossible, @@ -110,7 +112,7 @@ export default function EditGasDisplay({ return (
- {warning && ( + {warning && !isGasEstimatesLoading && (
)} - {showTopError && ( + {showTopError && !isGasEstimatesLoading && (
)} - {requireDappAcknowledgement && ( + {requireDappAcknowledgement && !isGasEstimatesLoading && (
export const getCustomMaxPriorityFeePerGas = (state) => state.metamask.swapsState.customMaxPriorityFeePerGas; +export const getSwapsUserFeeLevel = (state) => + state.metamask.swapsState.swapsUserFeeLevel; + export const getFetchParams = (state) => state.metamask.swapsState.fetchParams; export const getQuotes = (state) => state.metamask.swapsState.quotes; diff --git a/ui/helpers/utils/conversions.util.js b/ui/helpers/utils/conversions.util.js index 78a28293b..cdfee12b9 100644 --- a/ui/helpers/utils/conversions.util.js +++ b/ui/helpers/utils/conversions.util.js @@ -172,7 +172,7 @@ export function addHexes(aHexWEI, bHexWEI) { } export function subtractHexes(aHexWEI, bHexWEI) { - return addCurrencies(aHexWEI, bHexWEI, { + return subtractCurrencies(aHexWEI, bHexWEI, { aBase: 16, bBase: 16, toNumericBase: 'hex', diff --git a/ui/hooks/useGasFeeInputs.js b/ui/hooks/useGasFeeInputs.js index 8dda30e21..37da6a796 100644 --- a/ui/hooks/useGasFeeInputs.js +++ b/ui/hooks/useGasFeeInputs.js @@ -1,7 +1,7 @@ import { addHexPrefix } from 'ethereumjs-util'; import { useCallback, useState } from 'react'; import { useSelector } from 'react-redux'; -import { findKey, isEqual } from 'lodash'; +import { isEqual } from 'lodash'; import { GAS_ESTIMATE_TYPES, EDIT_GAS_MODES, @@ -103,32 +103,6 @@ function getGasFeeEstimate( return String(fallback); } -/** - * This method tries to determine if any estimate level matches the - * current maxFeePerGas and maxPriorityFeePerGas values. If we find - * a match, we can pre-select a radio button in the RadioGroup - */ -function getMatchingEstimateFromGasFees( - gasFeeEstimates, - maxFeePerGas, - maxPriorityFeePerGas, - gasPrice, - networkAndAccountSupports1559, -) { - return ( - findKey(gasFeeEstimates, (estimate) => { - if (networkAndAccountSupports1559) { - return ( - Number(estimate?.suggestedMaxPriorityFeePerGas) === - Number(maxPriorityFeePerGas) && - Number(estimate?.suggestedMaxFeePerGas) === Number(maxFeePerGas) - ); - } - return estimate?.gasPrice === gasPrice; - }) || null - ); -} - /** * @typedef {Object} GasFeeInputReturnType * @property {DecGweiString} [maxFeePerGas] - the maxFeePerGas input value. @@ -229,33 +203,30 @@ export function useGasFeeInputs( ); const [initialMatchingEstimateLevel] = useState( - getMatchingEstimateFromGasFees( - gasFeeEstimates, - initialMaxFeePerGas, - initialMaxPriorityFeePerGas, - initialGasPrice, - networkAndAccountSupports1559, - ), + transaction?.userFeeLevel || null, ); + const initialFeeParamsAreCustom = + initialMatchingEstimateLevel === 'custom' || + initialMatchingEstimateLevel === null; // This hook keeps track of a few pieces of transitional state. It is // transitional because it is only used to modify a transaction in the // metamask (background) state tree. const [maxFeePerGas, setMaxFeePerGas] = useState( - initialMaxFeePerGas && !initialMatchingEstimateLevel + initialMaxFeePerGas && initialFeeParamsAreCustom ? initialMaxFeePerGas : null, ); const [maxPriorityFeePerGas, setMaxPriorityFeePerGas] = useState( - initialMaxPriorityFeePerGas && !initialMatchingEstimateLevel + initialMaxPriorityFeePerGas && initialFeeParamsAreCustom ? initialMaxPriorityFeePerGas : null, ); const [gasPriceHasBeenManuallySet, setGasPriceHasBeenManuallySet] = useState( - false, + initialMatchingEstimateLevel === 'custom', ); const [gasPrice, setGasPrice] = useState( - initialGasPrice && !initialMatchingEstimateLevel ? initialGasPrice : null, + initialGasPrice && initialFeeParamsAreCustom ? initialGasPrice : null, ); const [gasLimit, setGasLimit] = useState( Number(hexToDecimal(transaction?.txParams?.gas ?? minimumGasLimit)), @@ -265,7 +236,7 @@ export function useGasFeeInputs( const dontDefaultToAnEstimateLevel = userPrefersAdvancedGas && transaction?.txParams?.maxPriorityFeePerGas && - transaction?.txParams?.gasPrice; + transaction?.txParams?.maxFeePerGas; const initialEstimateToUse = transaction ? initialMatchingEstimateLevel @@ -514,28 +485,28 @@ export function useGasFeeInputs( { value: ethBalance, fromNumericBase: 'hex' }, ); + const handleGasLimitOutOfBoundError = useCallback(() => { + if (gasErrors.gasLimit === GAS_FORM_ERRORS.GAS_LIMIT_OUT_OF_BOUNDS) { + const transactionGasLimitDec = hexToDecimal(transaction?.txParams?.gas); + const minimumGasLimitDec = hexToDecimal(minimumGasLimit); + setGasLimit( + transactionGasLimitDec > minimumGasLimitDec + ? transactionGasLimitDec + : minimumGasLimitDec, + ); + } + }, [minimumGasLimit, gasErrors.gasLimit, transaction]); // When a user selects an estimate level, it will wipe out what they have // previously put in the inputs. This returns the inputs to the estimated // values at the level specified. - const setEstimateToUse = useCallback( - (estimateLevel) => { - setInternalEstimateToUse(estimateLevel); - if (gasErrors.gasLimit === GAS_FORM_ERRORS.GAS_LIMIT_OUT_OF_BOUNDS) { - const transactionGasLimit = hexToDecimal(transaction?.txParams?.gas); - const minimumGasLimitDec = hexToDecimal(minimumGasLimit); - setGasLimit( - transactionGasLimit > minimumGasLimitDec - ? transactionGasLimit - : minimumGasLimitDec, - ); - } - setMaxFeePerGas(null); - setMaxPriorityFeePerGas(null); - setGasPrice(null); - setGasPriceHasBeenManuallySet(false); - }, - [minimumGasLimit, gasErrors.gasLimit, transaction], - ); + const setEstimateToUse = (estimateLevel) => { + setInternalEstimateToUse(estimateLevel); + handleGasLimitOutOfBoundError(); + setMaxFeePerGas(null); + setMaxPriorityFeePerGas(null); + setGasPrice(null); + setGasPriceHasBeenManuallySet(false); + }; return { maxFeePerGas: maxFeePerGasToUse, @@ -561,7 +532,8 @@ export function useGasFeeInputs( gasErrors: errorsAndWarnings, hasGasErrors: hasBlockingGasErrors, onManualChange: () => { - setEstimateToUse(null); + setInternalEstimateToUse('custom'); + handleGasLimitOutOfBoundError(); // Restore existing values setGasPrice(gasPriceToUse); setGasLimit(gasLimit); diff --git a/ui/pages/confirm-token-transaction-base/confirm-token-transaction-base.container.js b/ui/pages/confirm-token-transaction-base/confirm-token-transaction-base.container.js index 58c093bea..aec55e531 100644 --- a/ui/pages/confirm-token-transaction-base/confirm-token-transaction-base.container.js +++ b/ui/pages/confirm-token-transaction-base/confirm-token-transaction-base.container.js @@ -13,7 +13,6 @@ import { getTokenValueParam, } from '../../helpers/utils/token-util'; import { hexWEIToDecETH } from '../../helpers/utils/conversions.util'; -import { getHexGasTotal } from '../../helpers/utils/confirm-tx.util'; import ConfirmTokenTransactionBase from './confirm-token-transaction-base.component'; const mapStateToProps = (state, ownProps) => { @@ -34,32 +33,28 @@ const mapStateToProps = (state, ownProps) => { const { txData: { id: transactionId, - txParams: { to: tokenAddress, data, maxFeePerGas, gasPrice, gas } = {}, + txParams: { to: tokenAddress, data } = {}, } = {}, } = confirmTransaction; - const ethTransactionTotalMaxAmount = Number( - hexWEIToDecETH( - getHexGasTotal({ - gasPrice: maxFeePerGas ?? gasPrice, - gasLimit: gas, - }), - ), - ).toFixed(6); - const transaction = currentNetworkTxList.find( ({ id }) => id === (Number(paramsTransactionId) || transactionId), ) || {}; - const { ethTransactionTotal, fiatTransactionTotal } = transactionFeeSelector( - state, - transaction, - ); + const { + ethTransactionTotal, + fiatTransactionTotal, + hexMaximumTransactionFee, + } = transactionFeeSelector(state, transaction); const tokens = getTokens(state); const currentToken = tokens?.find(({ address }) => tokenAddress === address); const { decimals, symbol: tokenSymbol } = currentToken || {}; + const ethTransactionTotalMaxAmount = Number( + hexWEIToDecETH(hexMaximumTransactionFee), + ).toFixed(6); + const tokenData = getTokenData(data); const tokenValue = getTokenValueParam(tokenData); const toAddress = getTokenAddressParam(tokenData); diff --git a/ui/pages/confirm-transaction-base/confirm-transaction-base.component.js b/ui/pages/confirm-transaction-base/confirm-transaction-base.component.js index c26671323..e15b80126 100644 --- a/ui/pages/confirm-transaction-base/confirm-transaction-base.component.js +++ b/ui/pages/confirm-transaction-base/confirm-transaction-base.component.js @@ -4,7 +4,6 @@ import { ENVIRONMENT_TYPE_NOTIFICATION } from '../../../shared/constants/app'; import { getEnvironmentType } from '../../../app/scripts/lib/util'; import ConfirmPageContainer from '../../components/app/confirm-page-container'; import { isBalanceSufficient } from '../send/send.utils'; -import { getHexGasTotal } from '../../helpers/utils/confirm-tx.util'; import { addHexes, hexToDecimal, @@ -110,6 +109,8 @@ export default class ConfirmTransactionBase extends Component { gasIsLoading: PropTypes.bool, primaryTotalTextOverrideMaxAmount: PropTypes.string, useNativeCurrencyAsPrimaryCurrency: PropTypes.bool, + maxFeePerGas: PropTypes.string, + maxPriorityFeePerGas: PropTypes.string, }; state = { @@ -275,6 +276,7 @@ export default class ConfirmTransactionBase extends Component { primaryTotalTextOverride, secondaryTotalTextOverride, hexMinimumTransactionFee, + hexMaximumTransactionFee, hexTransactionTotal, useNonceField, customNonceValue, @@ -283,8 +285,9 @@ export default class ConfirmTransactionBase extends Component { getNextNonce, txData, useNativeCurrencyAsPrimaryCurrency, - hexMaximumTransactionFee, primaryTotalTextOverrideMaxAmount, + maxFeePerGas, + maxPriorityFeePerGas, } = this.props; const { t } = this.context; @@ -305,14 +308,7 @@ export default class ConfirmTransactionBase extends Component { return ( ); @@ -467,9 +463,12 @@ export default class ConfirmTransactionBase extends Component { subTitle={ } />, @@ -611,6 +610,8 @@ export default class ConfirmTransactionBase extends Component { history, mostRecentOverviewPage, updateCustomNonce, + maxFeePerGas, + maxPriorityFeePerGas, } = this.props; const { submitting } = this.state; @@ -618,6 +619,20 @@ export default class ConfirmTransactionBase extends Component { return; } + if (maxFeePerGas) { + txData.txParams = { + ...txData.txParams, + maxFeePerGas, + }; + } + + if (maxPriorityFeePerGas) { + txData.txParams = { + ...txData.txParams, + maxPriorityFeePerGas, + }; + } + this.setState( { submitting: true, diff --git a/ui/pages/confirm-transaction-base/confirm-transaction-base.container.js b/ui/pages/confirm-transaction-base/confirm-transaction-base.container.js index 4c8120553..6ccaf561d 100644 --- a/ui/pages/confirm-transaction-base/confirm-transaction-base.container.js +++ b/ui/pages/confirm-transaction-base/confirm-transaction-base.container.js @@ -118,6 +118,7 @@ const mapStateToProps = (state, ownProps) => { hexMinimumTransactionFee, hexMaximumTransactionFee, hexTransactionTotal, + gasEstimationObject, } = transactionFeeSelector(state, transaction); if (transaction && transaction.simulationFails) { @@ -152,8 +153,9 @@ const mapStateToProps = (state, ownProps) => { } customNonceValue = getCustomNonceValue(state); const isEthGasPrice = getIsEthGasPriceFetched(state); - const noGasPrice = getNoGasPriceFetched(state); + const noGasPrice = !supportsEIP1599 && getNoGasPriceFetched(state); const { useNativeCurrencyAsPrimaryCurrency } = getPreferences(state); + return { balance, fromAddress, @@ -196,6 +198,8 @@ const mapStateToProps = (state, ownProps) => { supportsEIP1599, gasIsLoading: isGasEstimatesLoading || gasLoadingAnimationIsShowing, useNativeCurrencyAsPrimaryCurrency, + maxFeePerGas: gasEstimationObject.maxFeePerGas, + maxPriorityFeePerGas: gasEstimationObject.maxPriorityFeePerGas, }; }; diff --git a/ui/pages/swaps/view-quote/view-quote.js b/ui/pages/swaps/view-quote/view-quote.js index e71875cc6..cdfbf0195 100644 --- a/ui/pages/swaps/view-quote/view-quote.js +++ b/ui/pages/swaps/view-quote/view-quote.js @@ -26,6 +26,7 @@ import { getCustomSwapsGas, // Gas limit. getCustomMaxFeePerGas, getCustomMaxPriorityFeePerGas, + getSwapsUserFeeLevel, getDestinationTokenInfo, getUsedSwapsGasPrice, getTopQuote, @@ -128,6 +129,7 @@ export default function ViewQuote() { const customMaxGas = useSelector(getCustomSwapsGas); const customMaxFeePerGas = useSelector(getCustomMaxFeePerGas); const customMaxPriorityFeePerGas = useSelector(getCustomMaxPriorityFeePerGas); + const swapsUserFeeLevel = useSelector(getSwapsUserFeeLevel); const tokenConversionRates = useSelector(getTokenExchangeRates); const memoizedTokenConversionRates = useEqualityCheck(tokenConversionRates); const { balance: ethBalance } = useSelector(getSelectedAccount); @@ -153,7 +155,9 @@ export default function ViewQuote() { if (networkAndAccountSupports1559) { // For Swaps we want to get 'high' estimations by default. // eslint-disable-next-line react-hooks/rules-of-hooks - gasFeeInputs = useGasFeeInputs('high'); + gasFeeInputs = useGasFeeInputs('high', { + userFeeLevel: swapsUserFeeLevel || 'high', + }); } const { isBestQuote } = usedQuote; @@ -670,6 +674,7 @@ export default function ViewQuote() { {showEditGasPopover && networkAndAccountSupports1559 && ( { + await promisifiedBackground.setSwapsUserFeeLevel(swapsCustomUserFeeLevel); + await forceUpdateMetamaskState(dispatch); + }; +} + export function customSwapsGasParamsUpdated(gasLimit, gasPrice) { return async (dispatch) => { await promisifiedBackground.setSwapsTxGasPrice(gasPrice); From 1216dc95c07869070dc8e3ba7b3bbddfeff17ca2 Mon Sep 17 00:00:00 2001 From: Alex Donesky Date: Fri, 6 Aug 2021 16:37:18 -0500 Subject: [PATCH 41/55] Hide gasTiming on edit-gas-popover when form is in error (#11792) * Hide gasTiming on edit-gas-popover when form is in error * Show unknown processing error if maxFeePerGas is too low for network conditions * remove unnecessary change * remove unnecessary function wrapper --- .../edit-gas-display.component.js | 13 ++++--- .../edit-gas-popover.component.js | 2 ++ .../app/gas-timing/gas-timing.component.js | 36 +++++++++++++------ ui/hooks/useGasFeeInputs.js | 1 + 4 files changed, 38 insertions(+), 14 deletions(-) diff --git a/ui/components/app/edit-gas-display/edit-gas-display.component.js b/ui/components/app/edit-gas-display/edit-gas-display.component.js index 26c15ad9c..703c4de83 100644 --- a/ui/components/app/edit-gas-display/edit-gas-display.component.js +++ b/ui/components/app/edit-gas-display/edit-gas-display.component.js @@ -61,6 +61,7 @@ export default function EditGasDisplay({ setDappSuggestedGasFeeAcknowledged, warning, gasErrors, + gasWarnings, onManualChange, minimumGasLimit, balanceError, @@ -173,10 +174,13 @@ export default function EditGasDisplay({ ]) } timing={ - + hasGasErrors === false && ( + + ) } /> {requireDappAcknowledgement && ( @@ -306,6 +310,7 @@ EditGasDisplay.propTypes = { warning: PropTypes.string, transaction: PropTypes.object, gasErrors: PropTypes.object, + gasWarnings: PropTypes.object, onManualChange: PropTypes.func, minimumGasLimit: PropTypes.number, balanceError: PropTypes.bool, diff --git a/ui/components/app/edit-gas-popover/edit-gas-popover.component.js b/ui/components/app/edit-gas-popover/edit-gas-popover.component.js index 739d96b43..4c152c411 100644 --- a/ui/components/app/edit-gas-popover/edit-gas-popover.component.js +++ b/ui/components/app/edit-gas-popover/edit-gas-popover.component.js @@ -86,6 +86,7 @@ export default function EditGasPopover({ estimatedMaximumFiat, hasGasErrors, gasErrors, + gasWarnings, onManualChange, balanceError, estimatesUnavailableWarning, @@ -245,6 +246,7 @@ export default function EditGasPopover({ mode={mode} transaction={transaction} gasErrors={gasErrors} + gasWarnings={gasWarnings} onManualChange={onManualChange} minimumGasLimit={minimumGasLimitDec} balanceError={balanceError} diff --git a/ui/components/app/gas-timing/gas-timing.component.js b/ui/components/app/gas-timing/gas-timing.component.js index c00ddc5e2..4f34141f2 100644 --- a/ui/components/app/gas-timing/gas-timing.component.js +++ b/ui/components/app/gas-timing/gas-timing.component.js @@ -23,6 +23,7 @@ import { import InfoTooltip from '../../ui/info-tooltip/info-tooltip'; import { getGasFeeTimeEstimate } from '../../../store/actions'; +import { GAS_FORM_ERRORS } from '../../../helpers/constants/gas'; // Once we reach this second threshold, we switch to minutes as a unit const SECOND_CUTOFF = 90; @@ -38,12 +39,14 @@ const toHumanReadableTime = (milliseconds = 1, t) => { export default function GasTiming({ maxFeePerGas = 0, maxPriorityFeePerGas = 0, + gasWarnings, }) { const gasEstimateType = useSelector(getGasEstimateType); const gasFeeEstimates = useSelector(getGasFeeEstimates); const isGasEstimatesLoading = useSelector(getIsGasEstimatesLoading); const [customEstimatedTime, setCustomEstimatedTime] = useState(null); + const t = useContext(I18nContext); // If the user has chosen a value lower than the low gas fee estimate, // We'll need to use the useEffect hook below to make a call to calculate @@ -89,7 +92,27 @@ export default function GasTiming({ previousIsUnknownLow, ]); - const t = useContext(I18nContext); + const unknownProcessingTimeText = ( + <> + {t('editGasTooLow')}{' '} + + + ); + + if ( + gasWarnings?.maxPriorityFee === GAS_FORM_ERRORS.MAX_PRIORITY_FEE_TOO_LOW || + gasWarnings?.maxFee === GAS_FORM_ERRORS.MAX_FEE_TOO_LOW + ) { + return ( + + {unknownProcessingTimeText} + + ); + } // Don't show anything if we don't have enough information if ( @@ -137,15 +160,7 @@ export default function GasTiming({ customEstimatedTime?.upperTimeBound === 'unknown' ) { fontWeight = FONT_WEIGHT.BOLD; - text = ( - <> - {t('editGasTooLow')}{' '} - - - ); + text = unknownProcessingTimeText; } else { text = t('gasTimingNegative', [ toHumanReadableTime(Number(customEstimatedTime?.upperTimeBound), t), @@ -174,4 +189,5 @@ export default function GasTiming({ GasTiming.propTypes = { maxPriorityFeePerGas: PropTypes.string, maxFeePerGas: PropTypes.string, + gasWarnings: PropTypes.object, }; diff --git a/ui/hooks/useGasFeeInputs.js b/ui/hooks/useGasFeeInputs.js index 37da6a796..16dc9b263 100644 --- a/ui/hooks/useGasFeeInputs.js +++ b/ui/hooks/useGasFeeInputs.js @@ -531,6 +531,7 @@ export function useGasFeeInputs( estimatedGasFeeTimeBounds, gasErrors: errorsAndWarnings, hasGasErrors: hasBlockingGasErrors, + gasWarnings, onManualChange: () => { setInternalEstimateToUse('custom'); handleGasLimitOutOfBoundError(); From e6543a83effce673c22397f828f410dff74bace9 Mon Sep 17 00:00:00 2001 From: Dan J Miller Date: Fri, 6 Aug 2021 19:48:53 -0230 Subject: [PATCH 42/55] Ensure transaction activity log supports EIP-1559 (#11794) * Ensure transaction activity log supports EIP-1559 * unit test fix * Add estimated base fee to swaps txMeta * fix lint and tests * Improve activity log copy --- app/_locales/en/messages.json | 6 +-- app/scripts/controllers/transactions/index.js | 20 +++++++++- app/scripts/metamask-controller.js | 14 ++++++- .../edit-gas-popover.component.js | 14 ++++++- .../transaction-activity-log.util.js | 40 +++++++++++++++---- ui/ducks/swaps/swaps.js | 6 ++- ui/hooks/useGasFeeInputs.js | 1 + .../confirm-transaction-base.component.js | 6 +++ .../confirm-transaction-base.container.js | 1 + ui/store/actions.js | 14 ++++++- 10 files changed, 103 insertions(+), 19 deletions(-) diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index 4966e16b9..799c3236f 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -2499,7 +2499,7 @@ "message": "transaction" }, "transactionCancelAttempted": { - "message": "Transaction cancel attempted with gas fee of $1 at $2" + "message": "Transaction cancel attempted with estimated gas fee of $1 at $2" }, "transactionCancelSuccess": { "message": "Transaction successfully cancelled at $2" @@ -2557,10 +2557,10 @@ "message": "Total Gas Fee" }, "transactionResubmitted": { - "message": "Transaction resubmitted with gas fee increased to $1 at $2" + "message": "Transaction resubmitted with estimated gas fee increased to $1 at $2" }, "transactionSubmitted": { - "message": "Transaction submitted with gas fee of $1 at $2." + "message": "Transaction submitted with estimated gas fee of $1 at $2." }, "transactionUpdated": { "message": "Transaction updated at $2." diff --git a/app/scripts/controllers/transactions/index.js b/app/scripts/controllers/transactions/index.js index 9afa217e1..5a1d72611 100644 --- a/app/scripts/controllers/transactions/index.js +++ b/app/scripts/controllers/transactions/index.js @@ -693,7 +693,11 @@ export default class TransactionController extends EventEmitter { * params instead of allowing this method to generate them * @returns {txMeta} */ - async createCancelTransaction(originalTxId, customGasSettings) { + async createCancelTransaction( + originalTxId, + customGasSettings, + { estimatedBaseFee } = {}, + ) { const originalTxMeta = this.txStateManager.getTransaction(originalTxId); const { txParams } = originalTxMeta; const { from, nonce } = txParams; @@ -723,6 +727,10 @@ export default class TransactionController extends EventEmitter { type: TRANSACTION_TYPES.CANCEL, }); + if (estimatedBaseFee) { + newTxMeta.estimatedBaseFee = estimatedBaseFee; + } + this.addTransaction(newTxMeta); await this.approveTransaction(newTxMeta.id); return newTxMeta; @@ -738,7 +746,11 @@ export default class TransactionController extends EventEmitter { * params instead of allowing this method to generate them * @returns {txMeta} */ - async createSpeedUpTransaction(originalTxId, customGasSettings) { + async createSpeedUpTransaction( + originalTxId, + customGasSettings, + { estimatedBaseFee } = {}, + ) { const originalTxMeta = this.txStateManager.getTransaction(originalTxId); const { txParams } = originalTxMeta; @@ -758,6 +770,10 @@ export default class TransactionController extends EventEmitter { type: TRANSACTION_TYPES.RETRY, }); + if (estimatedBaseFee) { + newTxMeta.estimatedBaseFee = estimatedBaseFee; + } + this.addTransaction(newTxMeta); await this.approveTransaction(newTxMeta.id); return newTxMeta; diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index adef54dbb..05bfad876 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -2099,10 +2099,15 @@ export default class MetamaskController extends EventEmitter { * instead of allowing this method to generate them * @returns {Object} MetaMask state */ - async createCancelTransaction(originalTxId, customGasSettings) { + async createCancelTransaction( + originalTxId, + customGasSettings, + newTxMetaProps, + ) { await this.txController.createCancelTransaction( originalTxId, customGasSettings, + newTxMetaProps, ); const state = await this.getState(); return state; @@ -2119,10 +2124,15 @@ export default class MetamaskController extends EventEmitter { * instead of allowing this method to generate them * @returns {Object} MetaMask state */ - async createSpeedUpTransaction(originalTxId, customGasSettings) { + async createSpeedUpTransaction( + originalTxId, + customGasSettings, + newTxMetaProps, + ) { await this.txController.createSpeedUpTransaction( originalTxId, customGasSettings, + newTxMetaProps, ); const state = await this.getState(); return state; diff --git a/ui/components/app/edit-gas-popover/edit-gas-popover.component.js b/ui/components/app/edit-gas-popover/edit-gas-popover.component.js index 4c152c411..c3768b6ea 100644 --- a/ui/components/app/edit-gas-popover/edit-gas-popover.component.js +++ b/ui/components/app/edit-gas-popover/edit-gas-popover.component.js @@ -90,6 +90,7 @@ export default function EditGasPopover({ onManualChange, balanceError, estimatesUnavailableWarning, + estimatedBaseFee, } = useGasFeeInputs(defaultEstimateToUse, transaction, minimumGasLimit, mode); /** @@ -139,10 +140,18 @@ export default function EditGasPopover({ switch (mode) { case EDIT_GAS_MODES.CANCEL: - dispatch(createCancelTransaction(transaction.id, newGasSettings)); + dispatch( + createCancelTransaction(transaction.id, newGasSettings, { + estimatedBaseFee, + }), + ); break; case EDIT_GAS_MODES.SPEED_UP: - dispatch(createSpeedUpTransaction(transaction.id, newGasSettings)); + dispatch( + createSpeedUpTransaction(transaction.id, newGasSettings, { + estimatedBaseFee, + }), + ); break; case EDIT_GAS_MODES.MODIFY_IN_PLACE: dispatch(updateTransaction(updatedTxMeta)); @@ -170,6 +179,7 @@ export default function EditGasPopover({ maxPriorityFeePerGas, networkAndAccountSupport1559, estimateToUse, + estimatedBaseFee, ]); let title = t('editGasTitle'); diff --git a/ui/components/app/transaction-activity-log/transaction-activity-log.util.js b/ui/components/app/transaction-activity-log/transaction-activity-log.util.js index ee3d7ee0a..f33421280 100644 --- a/ui/components/app/transaction-activity-log/transaction-activity-log.util.js +++ b/ui/components/app/transaction-activity-log/transaction-activity-log.util.js @@ -1,5 +1,6 @@ import { TRANSACTION_TYPES } from '../../../../shared/constants/transaction'; import { getHexGasTotal } from '../../../helpers/utils/confirm-tx.util'; +import { sumHexes } from '../../../helpers/utils/transactions.util'; import { // event constants @@ -22,6 +23,7 @@ import { const STATUS_PATH = '/status'; const GAS_PRICE_PATH = '/txParams/gasPrice'; const GAS_LIMIT_PATH = '/txParams/gas'; +const ESTIMATE_BASE_FEE_PATH = '/estimatedBaseFee'; // op constants const REPLACE_OP = 'replace'; @@ -53,11 +55,20 @@ export function getActivities(transaction, isFirstTransaction = false) { metamaskNetworkId, hash, history = [], - txParams: { gas: paramsGasLimit, gasPrice: paramsGasPrice }, - xReceipt: { status } = {}, + txParams: { + gas: paramsGasLimit, + gasPrice: paramsGasPrice, + maxPriorityFeePerGas: paramsMaxPriorityFeePerGas, + }, + txReceipt: { status } = {}, type, + estimatedBaseFee: paramsEstimatedBaseFee, } = transaction; + const paramsEip1559Price = + paramsEstimatedBaseFee && + paramsMaxPriorityFeePerGas && + sumHexes(paramsEstimatedBaseFee, paramsMaxPriorityFeePerGas); let cachedGasLimit = '0x0'; let cachedGasPrice = '0x0'; @@ -66,13 +77,19 @@ export function getActivities(transaction, isFirstTransaction = false) { if (index === 0 && !Array.isArray(base) && base.txParams) { const { time: timestamp, - txParams: { value, gas = '0x0', gasPrice = '0x0' } = {}, + estimatedBaseFee, + txParams: { value, gas = '0x0', gasPrice, maxPriorityFeePerGas } = {}, } = base; + + const eip1559Price = + estimatedBaseFee && + maxPriorityFeePerGas && + sumHexes(estimatedBaseFee, maxPriorityFeePerGas); // The cached gas limit and gas price are used to display the gas fee in the activity log. We // need to cache these values because the status update history events don't provide us with // the latest gas limit and gas price. cachedGasLimit = gas; - cachedGasPrice = gasPrice; + cachedGasPrice = eip1559Price || gasPrice || '0x0'; if (isFirstTransaction) { return acc.concat({ @@ -95,14 +112,16 @@ export function getActivities(transaction, isFirstTransaction = false) { // timestamp, the first sub-entry in a history entry should. const timestamp = entryTimestamp || (base[0] && base[0].timestamp); - if (path in eventPathsHash && op === REPLACE_OP) { + const isAddBaseFee = path === ESTIMATE_BASE_FEE_PATH && op === 'add'; + + if ((path in eventPathsHash && op === REPLACE_OP) || isAddBaseFee) { switch (path) { case STATUS_PATH: { const gasFee = cachedGasLimit === '0x0' && cachedGasPrice === '0x0' ? getHexGasTotal({ gasLimit: paramsGasLimit, - gasPrice: paramsGasPrice, + gasPrice: paramsEip1559Price || paramsGasPrice, }) : getHexGasTotal({ gasLimit: cachedGasLimit, @@ -144,7 +163,8 @@ export function getActivities(transaction, isFirstTransaction = false) { // previously submitted event. These events happen when the gas limit and gas price is // changed at the confirm screen. case GAS_PRICE_PATH: - case GAS_LIMIT_PATH: { + case GAS_LIMIT_PATH: + case ESTIMATE_BASE_FEE_PATH: { const lastEvent = events[events.length - 1] || {}; const { lastEventKey } = lastEvent; @@ -152,6 +172,12 @@ export function getActivities(transaction, isFirstTransaction = false) { cachedGasLimit = value; } else if (path === GAS_PRICE_PATH) { cachedGasPrice = value; + } else if (path === ESTIMATE_BASE_FEE_PATH) { + cachedGasPrice = paramsEip1559Price || base?.txParams?.gasPrice; + lastEvent.value = getHexGasTotal({ + gasLimit: paramsGasLimit, + gasPrice: cachedGasPrice, + }); } if ( diff --git a/ui/ducks/swaps/swaps.js b/ui/ducks/swaps/swaps.js index 22eaed69c..d12d3c4ff 100644 --- a/ui/ducks/swaps/swaps.js +++ b/ui/ducks/swaps/swaps.js @@ -674,19 +674,21 @@ export const signAndSendTransactions = (history, metaMetricsEvent) => { let maxFeePerGas; let maxPriorityFeePerGas; let baseAndPriorityFeePerGas; + let decEstimatedBaseFee; if (networkAndAccountSupports1559) { const { high: { suggestedMaxFeePerGas, suggestedMaxPriorityFeePerGas }, estimatedBaseFee = '0', } = getGasFeeEstimates(state); + decEstimatedBaseFee = decGWEIToHexWEI(estimatedBaseFee); maxFeePerGas = customMaxFeePerGas || decGWEIToHexWEI(suggestedMaxFeePerGas); maxPriorityFeePerGas = customMaxPriorityFeePerGas || decGWEIToHexWEI(suggestedMaxPriorityFeePerGas); baseAndPriorityFeePerGas = addHexes( - decGWEIToHexWEI(estimatedBaseFee), + decEstimatedBaseFee, maxPriorityFeePerGas, ); } @@ -816,6 +818,7 @@ export const signAndSendTransactions = (history, metaMetricsEvent) => { updateTransaction( { ...approveTxMeta, + estimatedBaseFee: decEstimatedBaseFee, type: TRANSACTION_TYPES.SWAP_APPROVAL, sourceTokenSymbol: sourceTokenInfo.symbol, }, @@ -855,6 +858,7 @@ export const signAndSendTransactions = (history, metaMetricsEvent) => { updateTransaction( { ...tradeTxMeta, + estimatedBaseFee: decEstimatedBaseFee, sourceTokenSymbol: sourceTokenInfo.symbol, destinationTokenSymbol: destinationTokenInfo.symbol, type: TRANSACTION_TYPES.SWAP, diff --git a/ui/hooks/useGasFeeInputs.js b/ui/hooks/useGasFeeInputs.js index 16dc9b263..b153e3fff 100644 --- a/ui/hooks/useGasFeeInputs.js +++ b/ui/hooks/useGasFeeInputs.js @@ -544,5 +544,6 @@ export function useGasFeeInputs( }, balanceError, estimatesUnavailableWarning, + estimatedBaseFee: gasSettings.baseFeePerGas, }; } diff --git a/ui/pages/confirm-transaction-base/confirm-transaction-base.component.js b/ui/pages/confirm-transaction-base/confirm-transaction-base.component.js index e15b80126..3a3364466 100644 --- a/ui/pages/confirm-transaction-base/confirm-transaction-base.component.js +++ b/ui/pages/confirm-transaction-base/confirm-transaction-base.component.js @@ -111,6 +111,7 @@ export default class ConfirmTransactionBase extends Component { useNativeCurrencyAsPrimaryCurrency: PropTypes.bool, maxFeePerGas: PropTypes.string, maxPriorityFeePerGas: PropTypes.string, + baseFeePerGas: PropTypes.string, }; state = { @@ -612,6 +613,7 @@ export default class ConfirmTransactionBase extends Component { updateCustomNonce, maxFeePerGas, maxPriorityFeePerGas, + baseFeePerGas, } = this.props; const { submitting } = this.state; @@ -619,6 +621,10 @@ export default class ConfirmTransactionBase extends Component { return; } + if (baseFeePerGas) { + txData.estimatedBaseFee = baseFeePerGas; + } + if (maxFeePerGas) { txData.txParams = { ...txData.txParams, diff --git a/ui/pages/confirm-transaction-base/confirm-transaction-base.container.js b/ui/pages/confirm-transaction-base/confirm-transaction-base.container.js index 6ccaf561d..f10330399 100644 --- a/ui/pages/confirm-transaction-base/confirm-transaction-base.container.js +++ b/ui/pages/confirm-transaction-base/confirm-transaction-base.container.js @@ -200,6 +200,7 @@ const mapStateToProps = (state, ownProps) => { useNativeCurrencyAsPrimaryCurrency, maxFeePerGas: gasEstimationObject.maxFeePerGas, maxPriorityFeePerGas: gasEstimationObject.maxPriorityFeePerGas, + baseFeePerGas: gasEstimationObject.baseFeePerGas, }; }; diff --git a/ui/store/actions.js b/ui/store/actions.js index 594c10595..e265c923d 100644 --- a/ui/store/actions.js +++ b/ui/store/actions.js @@ -1340,7 +1340,11 @@ export function clearPendingTokens() { }; } -export function createCancelTransaction(txId, customGasSettings) { +export function createCancelTransaction( + txId, + customGasSettings, + newTxMetaProps, +) { log.debug('background.cancelTransaction'); let newTxId; @@ -1349,6 +1353,7 @@ export function createCancelTransaction(txId, customGasSettings) { background.createCancelTransaction( txId, customGasSettings, + newTxMetaProps, (err, newState) => { if (err) { dispatch(displayWarning(err.message)); @@ -1368,7 +1373,11 @@ export function createCancelTransaction(txId, customGasSettings) { }; } -export function createSpeedUpTransaction(txId, customGasSettings) { +export function createSpeedUpTransaction( + txId, + customGasSettings, + newTxMetaProps, +) { log.debug('background.createSpeedUpTransaction'); let newTx; @@ -1377,6 +1386,7 @@ export function createSpeedUpTransaction(txId, customGasSettings) { background.createSpeedUpTransaction( txId, customGasSettings, + newTxMetaProps, (err, newState) => { if (err) { dispatch(displayWarning(err.message)); From 2bb1c1a61df0dec1d82e7f10bcbdc3afdbc15620 Mon Sep 17 00:00:00 2001 From: David Walsh Date: Tue, 10 Aug 2021 09:25:07 -0500 Subject: [PATCH 43/55] Selected radio group label should be black (#11770) --- ui/components/ui/radio-group/radio-group.component.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/components/ui/radio-group/radio-group.component.js b/ui/components/ui/radio-group/radio-group.component.js index e1c2f27e8..31c98c6df 100644 --- a/ui/components/ui/radio-group/radio-group.component.js +++ b/ui/components/ui/radio-group/radio-group.component.js @@ -38,7 +38,7 @@ export default function RadioGroup({ options, name, selectedValue, onChange }) {
Date: Tue, 10 Aug 2021 15:11:28 -0500 Subject: [PATCH 44/55] bump path-parse version to address security vulnerability (#11807) --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index a75eb3829..425241749 100644 --- a/yarn.lock +++ b/yarn.lock @@ -21426,9 +21426,9 @@ path-key@^3.0.0, path-key@^3.1.0: integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== path-parse@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" - integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== path-platform@~0.11.15: version "0.11.15" From 504e74fbed986f416adb377c90557859aa421f41 Mon Sep 17 00:00:00 2001 From: ryanml Date: Tue, 10 Aug 2021 13:45:09 -0700 Subject: [PATCH 45/55] Fixing EditGasDisplay console errors (#11797) --- .../advanced-gas-controls/advanced-gas-controls.component.js | 2 +- .../app/edit-gas-display/edit-gas-display.component.js | 2 +- .../confirm-transaction-base.component.js | 3 +++ 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/ui/components/app/advanced-gas-controls/advanced-gas-controls.component.js b/ui/components/app/advanced-gas-controls/advanced-gas-controls.component.js index ee9f3429d..667fa5e8a 100644 --- a/ui/components/app/advanced-gas-controls/advanced-gas-controls.component.js +++ b/ui/components/app/advanced-gas-controls/advanced-gas-controls.component.js @@ -140,5 +140,5 @@ AdvancedGasControls.propTypes = { maxPriorityFeeFiat: PropTypes.string, maxFeeFiat: PropTypes.string, gasErrors: PropTypes.object, - minimumGasLimit: PropTypes.number, + minimumGasLimit: PropTypes.string, }; diff --git a/ui/components/app/edit-gas-display/edit-gas-display.component.js b/ui/components/app/edit-gas-display/edit-gas-display.component.js index 703c4de83..c2cd0b3e2 100644 --- a/ui/components/app/edit-gas-display/edit-gas-display.component.js +++ b/ui/components/app/edit-gas-display/edit-gas-display.component.js @@ -312,7 +312,7 @@ EditGasDisplay.propTypes = { gasErrors: PropTypes.object, gasWarnings: PropTypes.object, onManualChange: PropTypes.func, - minimumGasLimit: PropTypes.number, + minimumGasLimit: PropTypes.string, balanceError: PropTypes.bool, estimatesUnavailableWarning: PropTypes.bool, hasGasErrors: PropTypes.bool, diff --git a/ui/pages/confirm-transaction-base/confirm-transaction-base.component.js b/ui/pages/confirm-transaction-base/confirm-transaction-base.component.js index 3a3364466..ec0ca03ed 100644 --- a/ui/pages/confirm-transaction-base/confirm-transaction-base.component.js +++ b/ui/pages/confirm-transaction-base/confirm-transaction-base.component.js @@ -309,6 +309,7 @@ export default class ConfirmTransactionBase extends Component { return ( @@ -329,6 +330,7 @@ export default class ConfirmTransactionBase extends Component { return ( @@ -347,6 +349,7 @@ export default class ConfirmTransactionBase extends Component { return ( From da79ac382e21780e9aaeb71033b01486f45bd8a2 Mon Sep 17 00:00:00 2001 From: David Walsh Date: Tue, 10 Aug 2021 16:56:07 -0500 Subject: [PATCH 46/55] Autofocus the unit fiend in send screen (#11795) --- ui/components/ui/unit-input/unit-input.component.js | 1 + 1 file changed, 1 insertion(+) diff --git a/ui/components/ui/unit-input/unit-input.component.js b/ui/components/ui/unit-input/unit-input.component.js index 8ea172ea7..184d0ff7a 100644 --- a/ui/components/ui/unit-input/unit-input.component.js +++ b/ui/components/ui/unit-input/unit-input.component.js @@ -106,6 +106,7 @@ export default class UnitInput extends PureComponent { ref={(ref) => { this.unitInput = ref; }} + autoFocus /> {suffix &&
{suffix}
}
From 00572f5910542357c62937d9e95d5bfef1b7a559 Mon Sep 17 00:00:00 2001 From: David Walsh Date: Tue, 10 Aug 2021 16:56:16 -0500 Subject: [PATCH 47/55] Correct the vertical margins on the RadioGroup component (#11769) --- ui/components/ui/radio-group/index.scss | 6 +++- .../ui/radio-group/radio-group.component.js | 28 +++++++++++++------ 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/ui/components/ui/radio-group/index.scss b/ui/components/ui/radio-group/index.scss index 3c9685920..fc90ac975 100644 --- a/ui/components/ui/radio-group/index.scss +++ b/ui/components/ui/radio-group/index.scss @@ -1,9 +1,13 @@ .radio-group { display: grid; grid-template-columns: repeat(3, 1fr); - grid-template-rows: 100px; + grid-template-rows: 60px; width: 300px; + &--has-recommendation { + grid-template-rows: 100px; + } + label { cursor: pointer; } diff --git a/ui/components/ui/radio-group/radio-group.component.js b/ui/components/ui/radio-group/radio-group.component.js index 31c98c6df..eb53782fc 100644 --- a/ui/components/ui/radio-group/radio-group.component.js +++ b/ui/components/ui/radio-group/radio-group.component.js @@ -1,5 +1,6 @@ import React, { useContext } from 'react'; import PropTypes from 'prop-types'; +import classNames from 'classnames'; import { I18nContext } from '../../../contexts/i18n'; import Typography from '../typography/typography'; import { @@ -11,21 +12,30 @@ import { export default function RadioGroup({ options, name, selectedValue, onChange }) { const t = useContext(I18nContext); + const hasRecommendation = Boolean( + options.find((option) => option.recommended), + ); + return ( -
+
{options.map((option) => { const checked = option.value === selectedValue; return (