diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index 99c03131a..a61d51e17 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -2800,6 +2800,12 @@ "transactionDetailGasHeading": { "message": "Estimated gas fee" }, + "transactionDetailGasHeadingV2": { + "message": "Gas" + }, + "transactionDetailGasInfoV2": { + "message": "estimated" + }, "transactionDetailGasTooltipConversion": { "message": "Learn more about gas fees" }, diff --git a/development/build/scripts.js b/development/build/scripts.js index d8a22251e..cc916cf68 100644 --- a/development/build/scripts.js +++ b/development/build/scripts.js @@ -34,6 +34,7 @@ const metamaskrc = require('rc')('metamask', { INFURA_PROD_PROJECT_ID: process.env.INFURA_PROD_PROJECT_ID, ONBOARDING_V2: process.env.ONBOARDING_V2, COLLECTIBLES_V1: process.env.COLLECTIBLES_V1, + EIP_1559_V2: process.env.EIP_1559_V2, SEGMENT_HOST: process.env.SEGMENT_HOST, SEGMENT_WRITE_KEY: process.env.SEGMENT_WRITE_KEY, SEGMENT_BETA_WRITE_KEY: process.env.SEGMENT_BETA_WRITE_KEY, @@ -758,6 +759,7 @@ function getEnvironmentVariables({ buildType, devMode, testing }) { SWAPS_USE_DEV_APIS: process.env.SWAPS_USE_DEV_APIS === '1', ONBOARDING_V2: metamaskrc.ONBOARDING_V2 === '1', COLLECTIBLES_V1: metamaskrc.COLLECTIBLES_V1 === '1', + EIP_1559_V2: metamaskrc.EIP_1559_V2 === '1', }; } diff --git a/ui/components/app/gas-timing/gas-timing.component.js b/ui/components/app/gas-timing/gas-timing.component.js index 95e5bc24a..972805ffb 100644 --- a/ui/components/app/gas-timing/gas-timing.component.js +++ b/ui/components/app/gas-timing/gas-timing.component.js @@ -27,6 +27,8 @@ 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; +// eslint-disable-next-line prefer-destructuring +const EIP_1559_V2 = process.env.EIP_1559_V2; // Shows "seconds" as unit of time if under SECOND_CUTOFF, otherwise "minutes" const toHumanReadableTime = (milliseconds = 1, t) => { @@ -164,6 +166,12 @@ export default function GasTiming({ toHumanReadableTime(Number(customEstimatedTime?.upperTimeBound), t), ]); } + } + // code below needs to cleaned-up once EIP_1559_V2 flag is removed + else if (EIP_1559_V2) { + text = t('gasTimingNegative', [ + toHumanReadableTime(low.maxWaitTimeEstimate, t), + ]); } else { text = ( <> diff --git a/ui/components/app/transaction-detail-item/index.scss b/ui/components/app/transaction-detail-item/index.scss index a2dc8e4cd..8d6c1bcd7 100644 --- a/ui/components/app/transaction-detail-item/index.scss +++ b/ui/components/app/transaction-detail-item/index.scss @@ -1,5 +1,7 @@ .transaction-detail-item { color: $ui-4; + padding: 20px 0; + border-bottom: 1px solid $ui-3; &__row { display: flex; @@ -36,4 +38,21 @@ .currency-display-component { display: inline; } + + &:first-of-type { + padding-top: 0; + } + + &:last-of-type { + margin-bottom: 20px; + } + + &:first-child { + padding-top: 0; + } + + &:last-child { + padding-bottom: 0; + border-bottom: 0; + } } diff --git a/ui/components/app/transaction-detail/index.scss b/ui/components/app/transaction-detail/index.scss index 13c2601c8..cba477119 100644 --- a/ui/components/app/transaction-detail/index.scss +++ b/ui/components/app/transaction-detail/index.scss @@ -15,22 +15,4 @@ text-transform: uppercase; } } - - &-rows &-item:first-child { - padding-top: 0; - } - - &-item { - padding: 20px 0; - border-bottom: 1px solid $ui-3; - - &:first-child { - padding-top: 0; - } - - &:last-child { - padding-bottom: 0; - border-bottom: 0; - } - } } diff --git a/ui/components/ui/i18n-value/i18n-value.component.js b/ui/components/ui/i18n-value/i18n-value.component.js new file mode 100644 index 000000000..d321df7e9 --- /dev/null +++ b/ui/components/ui/i18n-value/i18n-value.component.js @@ -0,0 +1,15 @@ +import PropTypes from 'prop-types'; + +import { useI18nContext } from '../../../hooks/useI18nContext'; + +const I18nValue = ({ messageKey, options }) => { + const t = useI18nContext(); + return t(messageKey, options); +}; + +I18nValue.propTypes = { + messageKey: PropTypes.string.isRequired, + options: PropTypes.array, +}; + +export default I18nValue; diff --git a/ui/components/ui/i18n-value/index.js b/ui/components/ui/i18n-value/index.js new file mode 100644 index 000000000..190d57ab0 --- /dev/null +++ b/ui/components/ui/i18n-value/index.js @@ -0,0 +1 @@ +export { default } from './i18n-value.component'; diff --git a/ui/components/ui/typography/typography.js b/ui/components/ui/typography/typography.js index 02f9a3015..0af48e699 100644 --- a/ui/components/ui/typography/typography.js +++ b/ui/components/ui/typography/typography.js @@ -20,6 +20,7 @@ export default function Typography({ children, fontWeight = 'normal', fontStyle = 'normal', + fontSize, align, boxProps = {}, margin = [1, 0], @@ -33,6 +34,7 @@ export default function Typography({ { [`typography--align-${align}`]: Boolean(align), [`typography--color-${color}`]: Boolean(color), + [`typography--size-${fontSize}`]: Boolean(fontSize), }, ); @@ -67,6 +69,7 @@ Typography.propTypes = { margin: MultipleSizes, fontWeight: PropTypes.oneOf(Object.values(FONT_WEIGHT)), fontStyle: PropTypes.oneOf(Object.values(FONT_STYLE)), + fontSize: PropTypes.string, tag: PropTypes.oneOf([ 'p', 'h1', diff --git a/ui/components/ui/typography/typography.scss b/ui/components/ui/typography/typography.scss index 612bfc1cc..c92fc6eb2 100644 --- a/ui/components/ui/typography/typography.scss +++ b/ui/components/ui/typography/typography.scss @@ -32,6 +32,12 @@ } } + @each $size in design-system.$font-size { + &--size-#{$size} { + font-size: $size; + } + } + @each $alignment in design-system.$text-align { &--align-#{$alignment} { text-align: $alignment; diff --git a/ui/css/design-system/attributes.scss b/ui/css/design-system/attributes.scss index e9f03e053..8b920b7eb 100644 --- a/ui/css/design-system/attributes.scss +++ b/ui/css/design-system/attributes.scss @@ -82,3 +82,4 @@ $display: block, grid, flex, inline-block, inline-grid, inline-flex, list-item; $text-align: left, right, center, justify, end; $font-weight: bold, normal, 100, 200, 300, 400, 500, 600, 700, 800, 900; $font-style: normal, italic, oblique; +$font-size: 12px; 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 aa0482d7b..122efb895 100644 --- a/ui/pages/confirm-transaction-base/confirm-transaction-base.component.js +++ b/ui/pages/confirm-transaction-base/confirm-transaction-base.component.js @@ -52,6 +52,11 @@ import { import Typography from '../../components/ui/typography/typography'; import { MIN_GAS_LIMIT_DEC } from '../send/send.constants'; +import GasDetailsItem from './gas-details-item'; + +// eslint-disable-next-line prefer-destructuring +const EIP_1559_V2 = process.env.EIP_1559_V2; + const renderHeartBeatIfNotInTest = () => process.env.IN_TEST === 'true' ? null : ; @@ -408,114 +413,130 @@ export default class ConfirmTransactionBase extends Component { this.handleEditGas()} rows={[ - - {t('transactionDetailGasHeading')} - - - - - ) : ( - <> - {t('transactionDetailGasHeading')} - -

- {t('transactionDetailGasTooltipIntro', [ - isMainnet ? t('networkNameEthereum') : '', - ])} -

-

{t('transactionDetailGasTooltipExplanation')}

-

- - {t('transactionDetailGasTooltipConversion')} - -

- - } - position="top" - > - -
- - ) - } - detailTitleColor={COLORS.BLACK} - detailText={ -
- {renderHeartBeatIfNotInTest()} - -
- } - detailTotal={ -
- {renderHeartBeatIfNotInTest()} - -
- } - subText={t('editGasSubTextFee', [ - - {t('editGasSubTextFeeLabel')} - , -
- {renderHeartBeatIfNotInTest()} - -
, - ])} - subTitle={ - <> - {txData.dappSuggestedGasFees ? ( - - {t('transactionDetailDappGasMoreInfo')} - + EIP_1559_V2 ? ( + + ) : ( + + {t('transactionDetailGasHeading')} + + + + ) : ( - '' - )} - {supportsEIP1559 && ( - + {t('transactionDetailGasHeading')} + +

+ {t('transactionDetailGasTooltipIntro', [ + isMainnet ? t('networkNameEthereum') : '', + ])} +

+

{t('transactionDetailGasTooltipExplanation')}

+

+ + {t('transactionDetailGasTooltipConversion')} + +

+ + } + position="top" + > + +
+ + ) + } + detailTitleColor={COLORS.BLACK} + detailText={ +
+ {renderHeartBeatIfNotInTest()} + - )} - - } - />, +
+ } + detailTotal={ +
+ {renderHeartBeatIfNotInTest()} + +
+ } + subText={t('editGasSubTextFee', [ + + {t('editGasSubTextFeeLabel')} + , +
+ {renderHeartBeatIfNotInTest()} + +
, + ])} + subTitle={ + <> + {txData.dappSuggestedGasFees ? ( + + {t('transactionDetailDappGasMoreInfo')} + + ) : ( + '' + )} + {supportsEIP1559 && ( + + )} + + } + /> + ), + process.env.IN_TEST === 'true' ? null : ; + +const GasDetailItem = ({ + hexMaximumTransactionFee, + hexMinimumTransactionFee, + isMainnet, + maxFeePerGas, + maxPriorityFeePerGas, + supportsEIP1559, + txData, + useNativeCurrencyAsPrimaryCurrency, +}) => { + const t = useI18nContext(); + return ( + + + + + + () + + + + {t('transactionDetailGasTooltipIntro', [ + isMainnet ? t('networkNameEthereum') : '', + ])} + + + {t('transactionDetailGasTooltipExplanation')} + + + + {t('transactionDetailGasTooltipConversion')} + + + + } + position="top" + /> + + } + detailTitleColor={COLORS.BLACK} + detailText={ +
+ + +
+ } + detailTotal={ +
+ + +
+ } + subText={t('editGasSubTextFee', [ + + + + +
+ + +
+
, + ])} + subTitle={ + supportsEIP1559 && ( + + ) + } + /> + ); +}; + +GasDetailItem.propTypes = { + hexMaximumTransactionFee: PropTypes.string, + hexMinimumTransactionFee: PropTypes.string, + isMainnet: PropTypes.bool, + maxFeePerGas: PropTypes.string, + maxPriorityFeePerGas: PropTypes.string, + supportsEIP1559: PropTypes.bool, + txData: PropTypes.object, + useNativeCurrencyAsPrimaryCurrency: PropTypes.bool, +}; + +export default GasDetailItem; diff --git a/ui/pages/confirm-transaction-base/gas-details-item/gas-details-item.scss b/ui/pages/confirm-transaction-base/gas-details-item/gas-details-item.scss new file mode 100644 index 000000000..47d38af1f --- /dev/null +++ b/ui/pages/confirm-transaction-base/gas-details-item/gas-details-item.scss @@ -0,0 +1,17 @@ +.gas-details-item { + &__estimate { + font-weight: 400; + font-style: italic; + font-size: 12px; + color: $Grey-500; + line-height: inherit; + } + + &__gasfee-label { + font-weight: bold; + } + + &__currency-container { + position: relative; + } +} diff --git a/ui/pages/confirm-transaction-base/gas-details-item/gas-details-item.test.js b/ui/pages/confirm-transaction-base/gas-details-item/gas-details-item.test.js new file mode 100644 index 000000000..f94b70bbd --- /dev/null +++ b/ui/pages/confirm-transaction-base/gas-details-item/gas-details-item.test.js @@ -0,0 +1,41 @@ +import React from 'react'; +import { screen } from '@testing-library/react'; + +import { ETH } from '../../../helpers/constants/common'; +import { renderWithProvider } from '../../../../test/jest'; +import configureStore from '../../../store/store'; + +import GasDetailsItem from './gas-details-item'; + +const render = (props) => { + const store = configureStore({ + metamask: { + nativeCurrency: ETH, + preferences: { + useNativeCurrencyAsPrimaryCurrency: true, + }, + provider: {}, + }, + }); + + return renderWithProvider(, store); +}; + +describe('GasDetailsItem', () => { + it('should render label', () => { + render(); + expect(screen.queryByText('Gas')).toBeInTheDocument(); + expect(screen.queryByText('(estimated)')).toBeInTheDocument(); + expect(screen.queryByText('Max fee:')).toBeInTheDocument(); + }); + + it('should render gas fee details', () => { + render({ + hexMinimumTransactionFee: '0x1ca62a4f7800', + hexMaximumTransactionFee: '0x290ee75e3d900', + }); + expect(screen.queryAllByText('0.000031')).toHaveLength(2); + expect(screen.queryByText('ETH')).toBeInTheDocument(); + expect(screen.queryByText('0.000722')).toBeInTheDocument(); + }); +}); diff --git a/ui/pages/confirm-transaction-base/gas-details-item/index.js b/ui/pages/confirm-transaction-base/gas-details-item/index.js new file mode 100644 index 000000000..f6bacdbeb --- /dev/null +++ b/ui/pages/confirm-transaction-base/gas-details-item/index.js @@ -0,0 +1 @@ +export { default } from './gas-details-item'; diff --git a/ui/pages/pages.scss b/ui/pages/pages.scss index 300bdcf4a..df9de3f4e 100644 --- a/ui/pages/pages.scss +++ b/ui/pages/pages.scss @@ -5,6 +5,7 @@ @import 'confirm-approve/index'; @import 'confirm-decrypt-message/confirm-decrypt-message'; @import 'confirm-encryption-public-key/confirm-encryption-public-key'; +@import 'confirm-transaction-base/gas-details-item/gas-details-item'; @import 'confirmation/confirmation'; @import 'connected-sites/index'; @import 'connected-accounts/index';